roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
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 or 0 for hidden
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
775  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
777  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
778  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
779  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
780  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
781  *
782  * 
783  * @cfg {Boolean} hidden (true|false) hide the element
784  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
785  * @cfg {String} fa (ban|check|...) font awesome icon
786  * @cfg {Number} fasize (1|2|....) font awsome size
787
788  * @cfg {String} icon (info-sign|check|...) glyphicon name
789
790  * @cfg {String} html content of column.
791  * 
792  * @constructor
793  * Create a new Column
794  * @param {Object} config The config object
795  */
796
797 Roo.bootstrap.Column = function(config){
798     Roo.bootstrap.Column.superclass.constructor.call(this, config);
799 };
800
801 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
802     
803     xs: false,
804     sm: false,
805     md: false,
806     lg: false,
807     xsoff: false,
808     smoff: false,
809     mdoff: false,
810     lgoff: false,
811     html: '',
812     offset: 0,
813     alert: false,
814     fa: false,
815     icon : false,
816     hidden : false,
817     fasize : 1,
818     
819     getAutoCreate : function(){
820         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
821         
822         cfg = {
823             tag: 'div',
824             cls: 'column'
825         };
826         
827         var settings=this;
828         ['xs','sm','md','lg'].map(function(size){
829             //Roo.log( size + ':' + settings[size]);
830             
831             if (settings[size+'off'] !== false) {
832                 cfg.cls += ' col-' + settings[size+'off'] + '-offset';
833             }
834             
835             if (settings[size] === false) {
836                 return;
837             }
838             Roo.log(settings[size]);
839             if (!settings[size]) { // 0 = hidden
840                 cfg.cls += ' hidden-' + size;
841                 return;
842             }
843             cfg.cls += ' col-' + size + '-' + settings[size];
844             
845         });
846         
847         if (this.hidden) {
848             cfg.cls += ' hidden';
849         }
850         
851         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
852             cfg.cls +=' alert alert-' + this.alert;
853         }
854         
855         
856         if (this.html.length) {
857             cfg.html = this.html;
858         }
859         if (this.fa) {
860             var fasize = '';
861             if (this.fasize > 1) {
862                 fasize = ' fa-' + this.fasize + 'x';
863             }
864             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
865             
866             
867         }
868         if (this.icon) {
869             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
870         }
871         
872         return cfg;
873     }
874    
875 });
876
877  
878
879  /*
880  * - LGPL
881  *
882  * page container.
883  * 
884  */
885
886
887 /**
888  * @class Roo.bootstrap.Container
889  * @extends Roo.bootstrap.Component
890  * Bootstrap Container class
891  * @cfg {Boolean} jumbotron is it a jumbotron element
892  * @cfg {String} html content of element
893  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
894  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
895  * @cfg {String} header content of header (for panel)
896  * @cfg {String} footer content of footer (for panel)
897  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
898  * @cfg {String} tag (header|aside|section) type of HTML tag.
899  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
900  * @cfg {String} fa (ban|check|...) font awesome icon
901  * @cfg {String} icon (info-sign|check|...) glyphicon name
902  * @cfg {Boolean} hidden (true|false) hide the element
903
904  *     
905  * @constructor
906  * Create a new Container
907  * @param {Object} config The config object
908  */
909
910 Roo.bootstrap.Container = function(config){
911     Roo.bootstrap.Container.superclass.constructor.call(this, config);
912 };
913
914 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
915     
916     jumbotron : false,
917     well: '',
918     panel : '',
919     header: '',
920     footer : '',
921     sticky: '',
922     tag : false,
923     alert : false,
924     fa: false,
925     icon : false,
926   
927      
928     getChildContainer : function() {
929         
930         if(!this.el){
931             return false;
932         }
933         
934         if (this.panel.length) {
935             return this.el.select('.panel-body',true).first();
936         }
937         
938         return this.el;
939     },
940     
941     
942     getAutoCreate : function(){
943         
944         var cfg = {
945             tag : this.tag || 'div',
946             html : '',
947             cls : ''
948         };
949         if (this.jumbotron) {
950             cfg.cls = 'jumbotron';
951         }
952         
953         
954         
955         // - this is applied by the parent..
956         //if (this.cls) {
957         //    cfg.cls = this.cls + '';
958         //}
959         
960         if (this.sticky.length) {
961             
962             var bd = Roo.get(document.body);
963             if (!bd.hasClass('bootstrap-sticky')) {
964                 bd.addClass('bootstrap-sticky');
965                 Roo.select('html',true).setStyle('height', '100%');
966             }
967              
968             cfg.cls += 'bootstrap-sticky-' + this.sticky;
969         }
970         
971         
972         if (this.well.length) {
973             switch (this.well) {
974                 case 'lg':
975                 case 'sm':
976                     cfg.cls +=' well well-' +this.well;
977                     break;
978                 default:
979                     cfg.cls +=' well';
980                     break;
981             }
982         }
983         
984         if (this.hidden) {
985             cfg.cls += ' hidden';
986         }
987         
988         
989         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
990             cfg.cls +=' alert alert-' + this.alert;
991         }
992         
993         var body = cfg;
994         
995         if (this.panel.length) {
996             cfg.cls += ' panel panel-' + this.panel;
997             cfg.cn = [];
998             if (this.header.length) {
999                 cfg.cn.push({
1000                     
1001                     cls : 'panel-heading',
1002                     cn : [{
1003                         tag: 'h3',
1004                         cls : 'panel-title',
1005                         html : this.header
1006                     }]
1007                     
1008                 });
1009             }
1010             body = false;
1011             cfg.cn.push({
1012                 cls : 'panel-body',
1013                 html : this.html
1014             });
1015             
1016             
1017             if (this.footer.length) {
1018                 cfg.cn.push({
1019                     cls : 'panel-footer',
1020                     html : this.footer
1021                     
1022                 });
1023             }
1024             
1025         }
1026         
1027         if (body) {
1028             body.html = this.html || cfg.html;
1029             // prefix with the icons..
1030             if (this.fa) {
1031                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1032             }
1033             if (this.icon) {
1034                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1035             }
1036             
1037             
1038         }
1039         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1040             cfg.cls =  'container';
1041         }
1042         
1043         return cfg;
1044     },
1045     
1046     titleEl : function()
1047     {
1048         if(!this.el || !this.panel.length || !this.header.length){
1049             return;
1050         }
1051         
1052         return this.el.select('.panel-title',true).first();
1053     },
1054     
1055     setTitle : function(v)
1056     {
1057         var titleEl = this.titleEl();
1058         
1059         if(!titleEl){
1060             return;
1061         }
1062         
1063         titleEl.dom.innerHTML = v;
1064     },
1065     
1066     getTitle : function()
1067     {
1068         
1069         var titleEl = this.titleEl();
1070         
1071         if(!titleEl){
1072             return '';
1073         }
1074         
1075         return titleEl.dom.innerHTML;
1076     }
1077    
1078 });
1079
1080  /*
1081  * - LGPL
1082  *
1083  * image
1084  * 
1085  */
1086
1087
1088 /**
1089  * @class Roo.bootstrap.Img
1090  * @extends Roo.bootstrap.Component
1091  * Bootstrap Img class
1092  * @cfg {Boolean} imgResponsive false | true
1093  * @cfg {String} border rounded | circle | thumbnail
1094  * @cfg {String} src image source
1095  * @cfg {String} alt image alternative text
1096  * @cfg {String} href a tag href
1097  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1098  * 
1099  * @constructor
1100  * Create a new Input
1101  * @param {Object} config The config object
1102  */
1103
1104 Roo.bootstrap.Img = function(config){
1105     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1106     
1107     this.addEvents({
1108         // img events
1109         /**
1110          * @event click
1111          * The img click event for the img.
1112          * @param {Roo.EventObject} e
1113          */
1114         "click" : true
1115     });
1116 };
1117
1118 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1119     
1120     imgResponsive: true,
1121     border: '',
1122     src: '',
1123     href: false,
1124     target: false,
1125
1126     getAutoCreate : function(){
1127         
1128         var cfg = {
1129             tag: 'img',
1130             cls: (this.imgResponsive) ? 'img-responsive' : '',
1131             html : null
1132         }
1133         
1134         cfg.html = this.html || cfg.html;
1135         
1136         cfg.src = this.src || cfg.src;
1137         
1138         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1139             cfg.cls += ' img-' + this.border;
1140         }
1141         
1142         if(this.alt){
1143             cfg.alt = this.alt;
1144         }
1145         
1146         if(this.href){
1147             var a = {
1148                 tag: 'a',
1149                 href: this.href,
1150                 cn: [
1151                     cfg
1152                 ]
1153             }
1154             
1155             if(this.target){
1156                 a.target = this.target;
1157             }
1158             
1159         }
1160         
1161         
1162         return (this.href) ? a : cfg;
1163     },
1164     
1165     initEvents: function() {
1166         
1167         if(!this.href){
1168             this.el.on('click', this.onClick, this);
1169         }
1170     },
1171     
1172     onClick : function(e)
1173     {
1174         Roo.log('img onclick');
1175         this.fireEvent('click', this, e);
1176     }
1177    
1178 });
1179
1180  /*
1181  * - LGPL
1182  *
1183  * image
1184  * 
1185  */
1186
1187
1188 /**
1189  * @class Roo.bootstrap.Link
1190  * @extends Roo.bootstrap.Component
1191  * Bootstrap Link Class
1192  * @cfg {String} alt image alternative text
1193  * @cfg {String} href a tag href
1194  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1195  * @cfg {String} html the content of the link.
1196  * @cfg {Boolean} preventDefault (true | false) default false
1197
1198  * 
1199  * @constructor
1200  * Create a new Input
1201  * @param {Object} config The config object
1202  */
1203
1204 Roo.bootstrap.Link = function(config){
1205     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1206     
1207     this.addEvents({
1208         // img events
1209         /**
1210          * @event click
1211          * The img click event for the img.
1212          * @param {Roo.EventObject} e
1213          */
1214         "click" : true
1215     });
1216 };
1217
1218 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1219     
1220     href: false,
1221     target: false,
1222     preventDefault: false,
1223
1224     getAutoCreate : function(){
1225         
1226         var cfg = {
1227             tag: 'a',
1228             html : this.html || 'html-missing'
1229         }
1230         
1231         
1232         if(this.alt){
1233             cfg.alt = this.alt;
1234         }
1235         cfg.href = this.href || '#';
1236         if(this.target){
1237             cfg.target = this.target;
1238         }
1239         
1240         return cfg;
1241     },
1242     
1243     initEvents: function() {
1244         
1245         if(!this.href || this.preventDefault){
1246             this.el.on('click', this.onClick, this);
1247         }
1248     },
1249     
1250     onClick : function(e)
1251     {
1252         if(this.preventDefault){
1253             e.preventDefault();
1254         }
1255         //Roo.log('img onclick');
1256         this.fireEvent('click', this, e);
1257     }
1258    
1259 });
1260
1261  /*
1262  * - LGPL
1263  *
1264  * header
1265  * 
1266  */
1267
1268 /**
1269  * @class Roo.bootstrap.Header
1270  * @extends Roo.bootstrap.Component
1271  * Bootstrap Header class
1272  * @cfg {String} html content of header
1273  * @cfg {Number} level (1|2|3|4|5|6) default 1
1274  * 
1275  * @constructor
1276  * Create a new Header
1277  * @param {Object} config The config object
1278  */
1279
1280
1281 Roo.bootstrap.Header  = function(config){
1282     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1283 };
1284
1285 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1286     
1287     //href : false,
1288     html : false,
1289     level : 1,
1290     
1291     
1292     
1293     getAutoCreate : function(){
1294         
1295         var cfg = {
1296             tag: 'h' + (1 *this.level),
1297             html: this.html || 'fill in html'
1298         } ;
1299         
1300         return cfg;
1301     }
1302    
1303 });
1304
1305  
1306
1307  /*
1308  * Based on:
1309  * Ext JS Library 1.1.1
1310  * Copyright(c) 2006-2007, Ext JS, LLC.
1311  *
1312  * Originally Released Under LGPL - original licence link has changed is not relivant.
1313  *
1314  * Fork - LGPL
1315  * <script type="text/javascript">
1316  */
1317  
1318 /**
1319  * @class Roo.bootstrap.MenuMgr
1320  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1321  * @singleton
1322  */
1323 Roo.bootstrap.MenuMgr = function(){
1324    var menus, active, groups = {}, attached = false, lastShow = new Date();
1325
1326    // private - called when first menu is created
1327    function init(){
1328        menus = {};
1329        active = new Roo.util.MixedCollection();
1330        Roo.get(document).addKeyListener(27, function(){
1331            if(active.length > 0){
1332                hideAll();
1333            }
1334        });
1335    }
1336
1337    // private
1338    function hideAll(){
1339        if(active && active.length > 0){
1340            var c = active.clone();
1341            c.each(function(m){
1342                m.hide();
1343            });
1344        }
1345    }
1346
1347    // private
1348    function onHide(m){
1349        active.remove(m);
1350        if(active.length < 1){
1351            Roo.get(document).un("mouseup", onMouseDown);
1352             
1353            attached = false;
1354        }
1355    }
1356
1357    // private
1358    function onShow(m){
1359        var last = active.last();
1360        lastShow = new Date();
1361        active.add(m);
1362        if(!attached){
1363           Roo.get(document).on("mouseup", onMouseDown);
1364            
1365            attached = true;
1366        }
1367        if(m.parentMenu){
1368           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1369           m.parentMenu.activeChild = m;
1370        }else if(last && last.isVisible()){
1371           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1372        }
1373    }
1374
1375    // private
1376    function onBeforeHide(m){
1377        if(m.activeChild){
1378            m.activeChild.hide();
1379        }
1380        if(m.autoHideTimer){
1381            clearTimeout(m.autoHideTimer);
1382            delete m.autoHideTimer;
1383        }
1384    }
1385
1386    // private
1387    function onBeforeShow(m){
1388        var pm = m.parentMenu;
1389        if(!pm && !m.allowOtherMenus){
1390            hideAll();
1391        }else if(pm && pm.activeChild && active != m){
1392            pm.activeChild.hide();
1393        }
1394    }
1395
1396    // private
1397    function onMouseDown(e){
1398         Roo.log("on MouseDown");
1399         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1400            hideAll();
1401         }
1402         
1403         
1404    }
1405
1406    // private
1407    function onBeforeCheck(mi, state){
1408        if(state){
1409            var g = groups[mi.group];
1410            for(var i = 0, l = g.length; i < l; i++){
1411                if(g[i] != mi){
1412                    g[i].setChecked(false);
1413                }
1414            }
1415        }
1416    }
1417
1418    return {
1419
1420        /**
1421         * Hides all menus that are currently visible
1422         */
1423        hideAll : function(){
1424             hideAll();  
1425        },
1426
1427        // private
1428        register : function(menu){
1429            if(!menus){
1430                init();
1431            }
1432            menus[menu.id] = menu;
1433            menu.on("beforehide", onBeforeHide);
1434            menu.on("hide", onHide);
1435            menu.on("beforeshow", onBeforeShow);
1436            menu.on("show", onShow);
1437            var g = menu.group;
1438            if(g && menu.events["checkchange"]){
1439                if(!groups[g]){
1440                    groups[g] = [];
1441                }
1442                groups[g].push(menu);
1443                menu.on("checkchange", onCheck);
1444            }
1445        },
1446
1447         /**
1448          * Returns a {@link Roo.menu.Menu} object
1449          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1450          * be used to generate and return a new Menu instance.
1451          */
1452        get : function(menu){
1453            if(typeof menu == "string"){ // menu id
1454                return menus[menu];
1455            }else if(menu.events){  // menu instance
1456                return menu;
1457            }
1458            /*else if(typeof menu.length == 'number'){ // array of menu items?
1459                return new Roo.bootstrap.Menu({items:menu});
1460            }else{ // otherwise, must be a config
1461                return new Roo.bootstrap.Menu(menu);
1462            }
1463            */
1464            return false;
1465        },
1466
1467        // private
1468        unregister : function(menu){
1469            delete menus[menu.id];
1470            menu.un("beforehide", onBeforeHide);
1471            menu.un("hide", onHide);
1472            menu.un("beforeshow", onBeforeShow);
1473            menu.un("show", onShow);
1474            var g = menu.group;
1475            if(g && menu.events["checkchange"]){
1476                groups[g].remove(menu);
1477                menu.un("checkchange", onCheck);
1478            }
1479        },
1480
1481        // private
1482        registerCheckable : function(menuItem){
1483            var g = menuItem.group;
1484            if(g){
1485                if(!groups[g]){
1486                    groups[g] = [];
1487                }
1488                groups[g].push(menuItem);
1489                menuItem.on("beforecheckchange", onBeforeCheck);
1490            }
1491        },
1492
1493        // private
1494        unregisterCheckable : function(menuItem){
1495            var g = menuItem.group;
1496            if(g){
1497                groups[g].remove(menuItem);
1498                menuItem.un("beforecheckchange", onBeforeCheck);
1499            }
1500        }
1501    };
1502 }();/*
1503  * - LGPL
1504  *
1505  * menu
1506  * 
1507  */
1508
1509 /**
1510  * @class Roo.bootstrap.Menu
1511  * @extends Roo.bootstrap.Component
1512  * Bootstrap Menu class - container for MenuItems
1513  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1514  * 
1515  * @constructor
1516  * Create a new Menu
1517  * @param {Object} config The config object
1518  */
1519
1520
1521 Roo.bootstrap.Menu = function(config){
1522     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1523     if (this.registerMenu) {
1524         Roo.bootstrap.MenuMgr.register(this);
1525     }
1526     this.addEvents({
1527         /**
1528          * @event beforeshow
1529          * Fires before this menu is displayed
1530          * @param {Roo.menu.Menu} this
1531          */
1532         beforeshow : true,
1533         /**
1534          * @event beforehide
1535          * Fires before this menu is hidden
1536          * @param {Roo.menu.Menu} this
1537          */
1538         beforehide : true,
1539         /**
1540          * @event show
1541          * Fires after this menu is displayed
1542          * @param {Roo.menu.Menu} this
1543          */
1544         show : true,
1545         /**
1546          * @event hide
1547          * Fires after this menu is hidden
1548          * @param {Roo.menu.Menu} this
1549          */
1550         hide : true,
1551         /**
1552          * @event click
1553          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1554          * @param {Roo.menu.Menu} this
1555          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1556          * @param {Roo.EventObject} e
1557          */
1558         click : true,
1559         /**
1560          * @event mouseover
1561          * Fires when the mouse is hovering over this menu
1562          * @param {Roo.menu.Menu} this
1563          * @param {Roo.EventObject} e
1564          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1565          */
1566         mouseover : true,
1567         /**
1568          * @event mouseout
1569          * Fires when the mouse exits this menu
1570          * @param {Roo.menu.Menu} this
1571          * @param {Roo.EventObject} e
1572          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1573          */
1574         mouseout : true,
1575         /**
1576          * @event itemclick
1577          * Fires when a menu item contained in this menu is clicked
1578          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1579          * @param {Roo.EventObject} e
1580          */
1581         itemclick: true
1582     });
1583     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1584 };
1585
1586 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1587     
1588    /// html : false,
1589     //align : '',
1590     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1591     type: false,
1592     /**
1593      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1594      */
1595     registerMenu : true,
1596     
1597     menuItems :false, // stores the menu items..
1598     
1599     hidden:true,
1600     
1601     parentMenu : false,
1602     
1603     getChildContainer : function() {
1604         return this.el;  
1605     },
1606     
1607     getAutoCreate : function(){
1608          
1609         //if (['right'].indexOf(this.align)!==-1) {
1610         //    cfg.cn[1].cls += ' pull-right'
1611         //}
1612         
1613         
1614         var cfg = {
1615             tag : 'ul',
1616             cls : 'dropdown-menu' ,
1617             style : 'z-index:1000'
1618             
1619         }
1620         
1621         if (this.type === 'submenu') {
1622             cfg.cls = 'submenu active';
1623         }
1624         if (this.type === 'treeview') {
1625             cfg.cls = 'treeview-menu';
1626         }
1627         
1628         return cfg;
1629     },
1630     initEvents : function() {
1631         
1632        // Roo.log("ADD event");
1633        // Roo.log(this.triggerEl.dom);
1634         this.triggerEl.on('click', this.onTriggerPress, this);
1635         this.triggerEl.addClass('dropdown-toggle');
1636         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1637
1638         this.el.on("mouseover", this.onMouseOver, this);
1639         this.el.on("mouseout", this.onMouseOut, this);
1640         
1641         
1642     },
1643     findTargetItem : function(e){
1644         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1645         if(!t){
1646             return false;
1647         }
1648         //Roo.log(t);         Roo.log(t.id);
1649         if(t && t.id){
1650             //Roo.log(this.menuitems);
1651             return this.menuitems.get(t.id);
1652             
1653             //return this.items.get(t.menuItemId);
1654         }
1655         
1656         return false;
1657     },
1658     onClick : function(e){
1659         Roo.log("menu.onClick");
1660         var t = this.findTargetItem(e);
1661         if(!t){
1662             return;
1663         }
1664         Roo.log(e);
1665         /*
1666         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1667             if(t == this.activeItem && t.shouldDeactivate(e)){
1668                 this.activeItem.deactivate();
1669                 delete this.activeItem;
1670                 return;
1671             }
1672             if(t.canActivate){
1673                 this.setActiveItem(t, true);
1674             }
1675             return;
1676             
1677             
1678         }
1679         */
1680         Roo.log('pass click event');
1681         
1682         t.onClick(e);
1683         
1684         this.fireEvent("click", this, t, e);
1685         
1686         this.hide();
1687     },
1688      onMouseOver : function(e){
1689         var t  = this.findTargetItem(e);
1690         //Roo.log(t);
1691         //if(t){
1692         //    if(t.canActivate && !t.disabled){
1693         //        this.setActiveItem(t, true);
1694         //    }
1695         //}
1696         
1697         this.fireEvent("mouseover", this, e, t);
1698     },
1699     isVisible : function(){
1700         return !this.hidden;
1701     },
1702      onMouseOut : function(e){
1703         var t  = this.findTargetItem(e);
1704         
1705         //if(t ){
1706         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1707         //        this.activeItem.deactivate();
1708         //        delete this.activeItem;
1709         //    }
1710         //}
1711         this.fireEvent("mouseout", this, e, t);
1712     },
1713     
1714     
1715     /**
1716      * Displays this menu relative to another element
1717      * @param {String/HTMLElement/Roo.Element} element The element to align to
1718      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1719      * the element (defaults to this.defaultAlign)
1720      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1721      */
1722     show : function(el, pos, parentMenu){
1723         this.parentMenu = parentMenu;
1724         if(!this.el){
1725             this.render();
1726         }
1727         this.fireEvent("beforeshow", this);
1728         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1729     },
1730      /**
1731      * Displays this menu at a specific xy position
1732      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1733      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1734      */
1735     showAt : function(xy, parentMenu, /* private: */_e){
1736         this.parentMenu = parentMenu;
1737         if(!this.el){
1738             this.render();
1739         }
1740         if(_e !== false){
1741             this.fireEvent("beforeshow", this);
1742             
1743             //xy = this.el.adjustForConstraints(xy);
1744         }
1745         //this.el.setXY(xy);
1746         //this.el.show();
1747         this.hideMenuItems();
1748         this.hidden = false;
1749         this.triggerEl.addClass('open');
1750         this.focus();
1751         this.fireEvent("show", this);
1752     },
1753     
1754     focus : function(){
1755         return;
1756         if(!this.hidden){
1757             this.doFocus.defer(50, this);
1758         }
1759     },
1760
1761     doFocus : function(){
1762         if(!this.hidden){
1763             this.focusEl.focus();
1764         }
1765     },
1766
1767     /**
1768      * Hides this menu and optionally all parent menus
1769      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1770      */
1771     hide : function(deep){
1772         
1773         this.hideMenuItems();
1774         if(this.el && this.isVisible()){
1775             this.fireEvent("beforehide", this);
1776             if(this.activeItem){
1777                 this.activeItem.deactivate();
1778                 this.activeItem = null;
1779             }
1780             this.triggerEl.removeClass('open');;
1781             this.hidden = true;
1782             this.fireEvent("hide", this);
1783         }
1784         if(deep === true && this.parentMenu){
1785             this.parentMenu.hide(true);
1786         }
1787     },
1788     
1789     onTriggerPress  : function(e)
1790     {
1791         
1792         Roo.log('trigger press');
1793         //Roo.log(e.getTarget());
1794        // Roo.log(this.triggerEl.dom);
1795         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1796             return;
1797         }
1798         if (this.isVisible()) {
1799             Roo.log('hide');
1800             this.hide();
1801         } else {
1802             this.show(this.triggerEl, false, false);
1803         }
1804         
1805         
1806     },
1807     
1808          
1809        
1810     
1811     hideMenuItems : function()
1812     {
1813         //$(backdrop).remove()
1814         Roo.select('.open',true).each(function(aa) {
1815             
1816             aa.removeClass('open');
1817           //var parent = getParent($(this))
1818           //var relatedTarget = { relatedTarget: this }
1819           
1820            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1821           //if (e.isDefaultPrevented()) return
1822            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1823         })
1824     },
1825     addxtypeChild : function (tree, cntr) {
1826         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1827           
1828         this.menuitems.add(comp);
1829         return comp;
1830
1831     },
1832     getEl : function()
1833     {
1834         Roo.log(this.el);
1835         return this.el;
1836     }
1837 });
1838
1839  
1840  /*
1841  * - LGPL
1842  *
1843  * menu item
1844  * 
1845  */
1846
1847
1848 /**
1849  * @class Roo.bootstrap.MenuItem
1850  * @extends Roo.bootstrap.Component
1851  * Bootstrap MenuItem class
1852  * @cfg {String} html the menu label
1853  * @cfg {String} href the link
1854  * @cfg {Boolean} preventDefault (true | false) default true
1855  * 
1856  * 
1857  * @constructor
1858  * Create a new MenuItem
1859  * @param {Object} config The config object
1860  */
1861
1862
1863 Roo.bootstrap.MenuItem = function(config){
1864     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1865     this.addEvents({
1866         // raw events
1867         /**
1868          * @event click
1869          * The raw click event for the entire grid.
1870          * @param {Roo.EventObject} e
1871          */
1872         "click" : true
1873     });
1874 };
1875
1876 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1877     
1878     href : false,
1879     html : false,
1880     preventDefault: true,
1881     
1882     getAutoCreate : function(){
1883         var cfg= {
1884             tag: 'li',
1885             cls: 'dropdown-menu-item',
1886             cn: [
1887                     {
1888                         tag : 'a',
1889                         href : '#',
1890                         html : 'Link'
1891                     }
1892                 ]
1893         };
1894         if (this.parent().type == 'treeview') {
1895             cfg.cls = 'treeview-menu';
1896         }
1897         
1898         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1899         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1900         return cfg;
1901     },
1902     
1903     initEvents: function() {
1904         
1905         //this.el.select('a').on('click', this.onClick, this);
1906         
1907     },
1908     onClick : function(e)
1909     {
1910         Roo.log('item on click ');
1911         //if(this.preventDefault){
1912         //    e.preventDefault();
1913         //}
1914         //this.parent().hideMenuItems();
1915         
1916         this.fireEvent('click', this, e);
1917     },
1918     getEl : function()
1919     {
1920         return this.el;
1921     }
1922 });
1923
1924  
1925
1926  /*
1927  * - LGPL
1928  *
1929  * menu separator
1930  * 
1931  */
1932
1933
1934 /**
1935  * @class Roo.bootstrap.MenuSeparator
1936  * @extends Roo.bootstrap.Component
1937  * Bootstrap MenuSeparator class
1938  * 
1939  * @constructor
1940  * Create a new MenuItem
1941  * @param {Object} config The config object
1942  */
1943
1944
1945 Roo.bootstrap.MenuSeparator = function(config){
1946     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1947 };
1948
1949 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1950     
1951     getAutoCreate : function(){
1952         var cfg = {
1953             cls: 'divider',
1954             tag : 'li'
1955         };
1956         
1957         return cfg;
1958     }
1959    
1960 });
1961
1962  
1963
1964  
1965 /*
1966 <div class="modal fade">
1967   <div class="modal-dialog">
1968     <div class="modal-content">
1969       <div class="modal-header">
1970         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1971         <h4 class="modal-title">Modal title</h4>
1972       </div>
1973       <div class="modal-body">
1974         <p>One fine body&hellip;</p>
1975       </div>
1976       <div class="modal-footer">
1977         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1978         <button type="button" class="btn btn-primary">Save changes</button>
1979       </div>
1980     </div><!-- /.modal-content -->
1981   </div><!-- /.modal-dialog -->
1982 </div><!-- /.modal -->
1983 */
1984 /*
1985  * - LGPL
1986  *
1987  * page contgainer.
1988  * 
1989  */
1990
1991 /**
1992  * @class Roo.bootstrap.Modal
1993  * @extends Roo.bootstrap.Component
1994  * Bootstrap Modal class
1995  * @cfg {String} title Title of dialog
1996  * @cfg {Boolean} specificTitle (true|false) default false
1997  * @cfg {Array} buttons Array of buttons or standard button set..
1998  * @cfg {String} buttonPosition (left|right|center) default right
1999  * 
2000  * @constructor
2001  * Create a new Modal Dialog
2002  * @param {Object} config The config object
2003  */
2004
2005 Roo.bootstrap.Modal = function(config){
2006     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2007     this.addEvents({
2008         // raw events
2009         /**
2010          * @event btnclick
2011          * The raw btnclick event for the button
2012          * @param {Roo.EventObject} e
2013          */
2014         "btnclick" : true
2015     });
2016     this.buttons = this.buttons || [];
2017 };
2018
2019 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2020     
2021     title : 'test dialog',
2022    
2023     buttons : false,
2024     
2025     // set on load...
2026     body:  false,
2027     
2028     specificTitle: false,
2029     
2030     buttonPosition: 'right',
2031     
2032     onRender : function(ct, position)
2033     {
2034         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2035      
2036         if(!this.el){
2037             var cfg = Roo.apply({},  this.getAutoCreate());
2038             cfg.id = Roo.id();
2039             //if(!cfg.name){
2040             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2041             //}
2042             //if (!cfg.name.length) {
2043             //    delete cfg.name;
2044            // }
2045             if (this.cls) {
2046                 cfg.cls += ' ' + this.cls;
2047             }
2048             if (this.style) {
2049                 cfg.style = this.style;
2050             }
2051             this.el = Roo.get(document.body).createChild(cfg, position);
2052         }
2053         //var type = this.el.dom.type;
2054         
2055         if(this.tabIndex !== undefined){
2056             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2057         }
2058         
2059         
2060         
2061         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2062         this.maskEl.enableDisplayMode("block");
2063         this.maskEl.hide();
2064         //this.el.addClass("x-dlg-modal");
2065     
2066         if (this.buttons.length) {
2067             Roo.each(this.buttons, function(bb) {
2068                 b = Roo.apply({}, bb);
2069                 b.xns = b.xns || Roo.bootstrap;
2070                 b.xtype = b.xtype || 'Button';
2071                 if (typeof(b.listeners) == 'undefined') {
2072                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2073                 }
2074                 
2075                 var btn = Roo.factory(b);
2076                 
2077                 btn.onRender(this.el.select('.modal-footer div').first());
2078                 
2079             },this);
2080         }
2081         // render the children.
2082         var nitems = [];
2083         
2084         if(typeof(this.items) != 'undefined'){
2085             var items = this.items;
2086             delete this.items;
2087
2088             for(var i =0;i < items.length;i++) {
2089                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2090             }
2091         }
2092         
2093         this.items = nitems;
2094         
2095         this.body = this.el.select('.modal-body',true).first();
2096         this.close = this.el.select('.modal-header .close', true).first();
2097         this.footer = this.el.select('.modal-footer',true).first();
2098         this.initEvents();
2099         //this.el.addClass([this.fieldClass, this.cls]);
2100         
2101     },
2102     getAutoCreate : function(){
2103         
2104         
2105         var bdy = {
2106                 cls : 'modal-body',
2107                 html : this.html || ''
2108         };
2109         
2110         var title = {
2111             tag: 'h4',
2112             cls : 'modal-title',
2113             html : this.title
2114         };
2115         
2116         if(this.specificTitle){
2117             title = this.title;
2118         };
2119         
2120         return modal = {
2121             cls: "modal fade",
2122             style : 'display: none',
2123             cn : [
2124                 {
2125                     cls: "modal-dialog",
2126                     cn : [
2127                         {
2128                             cls : "modal-content",
2129                             cn : [
2130                                 {
2131                                     cls : 'modal-header',
2132                                     cn : [
2133                                         {
2134                                             tag: 'button',
2135                                             cls : 'close',
2136                                             html : '&times'
2137                                         },
2138                                         title
2139                                     ]
2140                                 },
2141                                 bdy,
2142                                 {
2143                                     cls : 'modal-footer',
2144                                     cn : [
2145                                         {
2146                                             tag: 'div',
2147                                             cls: 'btn-' + this.buttonPosition
2148                                         }
2149                                     ]
2150                                     
2151                                 }
2152                                 
2153                                 
2154                             ]
2155                             
2156                         }
2157                     ]
2158                         
2159                 }
2160             ]
2161             
2162             
2163         };
2164           
2165     },
2166     getChildContainer : function() {
2167          
2168          return this.el.select('.modal-body',true).first();
2169         
2170     },
2171     getButtonContainer : function() {
2172          return this.el.select('.modal-footer div',true).first();
2173         
2174     },
2175     initEvents : function()
2176     {
2177         this.el.select('.modal-header .close').on('click', this.hide, this);
2178 //        
2179 //        this.addxtype(this);
2180     },
2181     show : function() {
2182         
2183         if (!this.rendered) {
2184             this.render();
2185         }
2186        
2187         this.el.addClass('on');
2188         this.el.removeClass('fade');
2189         this.el.setStyle('display', 'block');
2190         Roo.get(document.body).addClass("x-body-masked");
2191         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2192         this.maskEl.show();
2193         this.el.setStyle('zIndex', '10001');
2194         this.fireEvent('show', this);
2195         
2196         
2197     },
2198     hide : function()
2199     {
2200         Roo.log('Modal hide?!');
2201         this.maskEl.hide();
2202         Roo.get(document.body).removeClass("x-body-masked");
2203         this.el.removeClass('on');
2204         this.el.addClass('fade');
2205         this.el.setStyle('display', 'none');
2206         this.fireEvent('hide', this);
2207     },
2208     
2209     addButton : function(str, cb)
2210     {
2211          
2212         
2213         var b = Roo.apply({}, { html : str } );
2214         b.xns = b.xns || Roo.bootstrap;
2215         b.xtype = b.xtype || 'Button';
2216         if (typeof(b.listeners) == 'undefined') {
2217             b.listeners = { click : cb.createDelegate(this)  };
2218         }
2219         
2220         var btn = Roo.factory(b);
2221            
2222         btn.onRender(this.el.select('.modal-footer div').first());
2223         
2224         return btn;   
2225        
2226     },
2227     
2228     setDefaultButton : function(btn)
2229     {
2230         //this.el.select('.modal-footer').()
2231     },
2232     resizeTo: function(w,h)
2233     {
2234         // skip..
2235     },
2236     setContentSize  : function(w, h)
2237     {
2238         
2239     },
2240     onButtonClick: function(btn,e)
2241     {
2242         //Roo.log([a,b,c]);
2243         this.fireEvent('btnclick', btn.name, e);
2244     },
2245     setTitle: function(str) {
2246         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2247         
2248     }
2249 });
2250
2251
2252 Roo.apply(Roo.bootstrap.Modal,  {
2253     /**
2254          * Button config that displays a single OK button
2255          * @type Object
2256          */
2257         OK :  [{
2258             name : 'ok',
2259             weight : 'primary',
2260             html : 'OK'
2261         }], 
2262         /**
2263          * Button config that displays Yes and No buttons
2264          * @type Object
2265          */
2266         YESNO : [
2267             {
2268                 name  : 'no',
2269                 html : 'No'
2270             },
2271             {
2272                 name  :'yes',
2273                 weight : 'primary',
2274                 html : 'Yes'
2275             }
2276         ],
2277         
2278         /**
2279          * Button config that displays OK and Cancel buttons
2280          * @type Object
2281          */
2282         OKCANCEL : [
2283             {
2284                name : 'cancel',
2285                 html : 'Cancel'
2286             },
2287             {
2288                 name : 'ok',
2289                 weight : 'primary',
2290                 html : 'OK'
2291             }
2292         ],
2293         /**
2294          * Button config that displays Yes, No and Cancel buttons
2295          * @type Object
2296          */
2297         YESNOCANCEL : [
2298             {
2299                 name : 'yes',
2300                 weight : 'primary',
2301                 html : 'Yes'
2302             },
2303             {
2304                 name : 'no',
2305                 html : 'No'
2306             },
2307             {
2308                 name : 'cancel',
2309                 html : 'Cancel'
2310             }
2311         ]
2312 });
2313  /*
2314  * - LGPL
2315  *
2316  * messagebox - can be used as a replace
2317  * 
2318  */
2319 /**
2320  * @class Roo.MessageBox
2321  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2322  * Example usage:
2323  *<pre><code>
2324 // Basic alert:
2325 Roo.Msg.alert('Status', 'Changes saved successfully.');
2326
2327 // Prompt for user data:
2328 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2329     if (btn == 'ok'){
2330         // process text value...
2331     }
2332 });
2333
2334 // Show a dialog using config options:
2335 Roo.Msg.show({
2336    title:'Save Changes?',
2337    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2338    buttons: Roo.Msg.YESNOCANCEL,
2339    fn: processResult,
2340    animEl: 'elId'
2341 });
2342 </code></pre>
2343  * @singleton
2344  */
2345 Roo.bootstrap.MessageBox = function(){
2346     var dlg, opt, mask, waitTimer;
2347     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2348     var buttons, activeTextEl, bwidth;
2349
2350     
2351     // private
2352     var handleButton = function(button){
2353         dlg.hide();
2354         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2355     };
2356
2357     // private
2358     var handleHide = function(){
2359         if(opt && opt.cls){
2360             dlg.el.removeClass(opt.cls);
2361         }
2362         //if(waitTimer){
2363         //    Roo.TaskMgr.stop(waitTimer);
2364         //    waitTimer = null;
2365         //}
2366     };
2367
2368     // private
2369     var updateButtons = function(b){
2370         var width = 0;
2371         if(!b){
2372             buttons["ok"].hide();
2373             buttons["cancel"].hide();
2374             buttons["yes"].hide();
2375             buttons["no"].hide();
2376             //dlg.footer.dom.style.display = 'none';
2377             return width;
2378         }
2379         dlg.footer.dom.style.display = '';
2380         for(var k in buttons){
2381             if(typeof buttons[k] != "function"){
2382                 if(b[k]){
2383                     buttons[k].show();
2384                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2385                     width += buttons[k].el.getWidth()+15;
2386                 }else{
2387                     buttons[k].hide();
2388                 }
2389             }
2390         }
2391         return width;
2392     };
2393
2394     // private
2395     var handleEsc = function(d, k, e){
2396         if(opt && opt.closable !== false){
2397             dlg.hide();
2398         }
2399         if(e){
2400             e.stopEvent();
2401         }
2402     };
2403
2404     return {
2405         /**
2406          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2407          * @return {Roo.BasicDialog} The BasicDialog element
2408          */
2409         getDialog : function(){
2410            if(!dlg){
2411                 dlg = new Roo.bootstrap.Modal( {
2412                     //draggable: true,
2413                     //resizable:false,
2414                     //constraintoviewport:false,
2415                     //fixedcenter:true,
2416                     //collapsible : false,
2417                     //shim:true,
2418                     //modal: true,
2419                   //  width:400,
2420                   //  height:100,
2421                     //buttonAlign:"center",
2422                     closeClick : function(){
2423                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2424                             handleButton("no");
2425                         }else{
2426                             handleButton("cancel");
2427                         }
2428                     }
2429                 });
2430                 dlg.render();
2431                 dlg.on("hide", handleHide);
2432                 mask = dlg.mask;
2433                 //dlg.addKeyListener(27, handleEsc);
2434                 buttons = {};
2435                 this.buttons = buttons;
2436                 var bt = this.buttonText;
2437                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2438                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2439                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2440                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2441                 Roo.log(buttons)
2442                 bodyEl = dlg.body.createChild({
2443
2444                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2445                         '<textarea class="roo-mb-textarea"></textarea>' +
2446                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2447                 });
2448                 msgEl = bodyEl.dom.firstChild;
2449                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2450                 textboxEl.enableDisplayMode();
2451                 textboxEl.addKeyListener([10,13], function(){
2452                     if(dlg.isVisible() && opt && opt.buttons){
2453                         if(opt.buttons.ok){
2454                             handleButton("ok");
2455                         }else if(opt.buttons.yes){
2456                             handleButton("yes");
2457                         }
2458                     }
2459                 });
2460                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2461                 textareaEl.enableDisplayMode();
2462                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2463                 progressEl.enableDisplayMode();
2464                 var pf = progressEl.dom.firstChild;
2465                 if (pf) {
2466                     pp = Roo.get(pf.firstChild);
2467                     pp.setHeight(pf.offsetHeight);
2468                 }
2469                 
2470             }
2471             return dlg;
2472         },
2473
2474         /**
2475          * Updates the message box body text
2476          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2477          * the XHTML-compliant non-breaking space character '&amp;#160;')
2478          * @return {Roo.MessageBox} This message box
2479          */
2480         updateText : function(text){
2481             if(!dlg.isVisible() && !opt.width){
2482                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2483             }
2484             msgEl.innerHTML = text || '&#160;';
2485       
2486             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2487             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2488             var w = Math.max(
2489                     Math.min(opt.width || cw , this.maxWidth), 
2490                     Math.max(opt.minWidth || this.minWidth, bwidth)
2491             );
2492             if(opt.prompt){
2493                 activeTextEl.setWidth(w);
2494             }
2495             if(dlg.isVisible()){
2496                 dlg.fixedcenter = false;
2497             }
2498             // to big, make it scroll. = But as usual stupid IE does not support
2499             // !important..
2500             
2501             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2502                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2503                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2504             } else {
2505                 bodyEl.dom.style.height = '';
2506                 bodyEl.dom.style.overflowY = '';
2507             }
2508             if (cw > w) {
2509                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2510             } else {
2511                 bodyEl.dom.style.overflowX = '';
2512             }
2513             
2514             dlg.setContentSize(w, bodyEl.getHeight());
2515             if(dlg.isVisible()){
2516                 dlg.fixedcenter = true;
2517             }
2518             return this;
2519         },
2520
2521         /**
2522          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2523          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2524          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2525          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2526          * @return {Roo.MessageBox} This message box
2527          */
2528         updateProgress : function(value, text){
2529             if(text){
2530                 this.updateText(text);
2531             }
2532             if (pp) { // weird bug on my firefox - for some reason this is not defined
2533                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2534             }
2535             return this;
2536         },        
2537
2538         /**
2539          * Returns true if the message box is currently displayed
2540          * @return {Boolean} True if the message box is visible, else false
2541          */
2542         isVisible : function(){
2543             return dlg && dlg.isVisible();  
2544         },
2545
2546         /**
2547          * Hides the message box if it is displayed
2548          */
2549         hide : function(){
2550             if(this.isVisible()){
2551                 dlg.hide();
2552             }  
2553         },
2554
2555         /**
2556          * Displays a new message box, or reinitializes an existing message box, based on the config options
2557          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2558          * The following config object properties are supported:
2559          * <pre>
2560 Property    Type             Description
2561 ----------  ---------------  ------------------------------------------------------------------------------------
2562 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2563                                    closes (defaults to undefined)
2564 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2565                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2566 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2567                                    progress and wait dialogs will ignore this property and always hide the
2568                                    close button as they can only be closed programmatically.
2569 cls               String           A custom CSS class to apply to the message box element
2570 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2571                                    displayed (defaults to 75)
2572 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2573                                    function will be btn (the name of the button that was clicked, if applicable,
2574                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2575                                    Progress and wait dialogs will ignore this option since they do not respond to
2576                                    user actions and can only be closed programmatically, so any required function
2577                                    should be called by the same code after it closes the dialog.
2578 icon              String           A CSS class that provides a background image to be used as an icon for
2579                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2580 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2581 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2582 modal             Boolean          False to allow user interaction with the page while the message box is
2583                                    displayed (defaults to true)
2584 msg               String           A string that will replace the existing message box body text (defaults
2585                                    to the XHTML-compliant non-breaking space character '&#160;')
2586 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2587 progress          Boolean          True to display a progress bar (defaults to false)
2588 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2589 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2590 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2591 title             String           The title text
2592 value             String           The string value to set into the active textbox element if displayed
2593 wait              Boolean          True to display a progress bar (defaults to false)
2594 width             Number           The width of the dialog in pixels
2595 </pre>
2596          *
2597          * Example usage:
2598          * <pre><code>
2599 Roo.Msg.show({
2600    title: 'Address',
2601    msg: 'Please enter your address:',
2602    width: 300,
2603    buttons: Roo.MessageBox.OKCANCEL,
2604    multiline: true,
2605    fn: saveAddress,
2606    animEl: 'addAddressBtn'
2607 });
2608 </code></pre>
2609          * @param {Object} config Configuration options
2610          * @return {Roo.MessageBox} This message box
2611          */
2612         show : function(options)
2613         {
2614             
2615             // this causes nightmares if you show one dialog after another
2616             // especially on callbacks..
2617              
2618             if(this.isVisible()){
2619                 
2620                 this.hide();
2621                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2622                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2623                 Roo.log("New Dialog Message:" +  options.msg )
2624                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2625                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2626                 
2627             }
2628             var d = this.getDialog();
2629             opt = options;
2630             d.setTitle(opt.title || "&#160;");
2631             d.close.setDisplayed(opt.closable !== false);
2632             activeTextEl = textboxEl;
2633             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2634             if(opt.prompt){
2635                 if(opt.multiline){
2636                     textboxEl.hide();
2637                     textareaEl.show();
2638                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2639                         opt.multiline : this.defaultTextHeight);
2640                     activeTextEl = textareaEl;
2641                 }else{
2642                     textboxEl.show();
2643                     textareaEl.hide();
2644                 }
2645             }else{
2646                 textboxEl.hide();
2647                 textareaEl.hide();
2648             }
2649             progressEl.setDisplayed(opt.progress === true);
2650             this.updateProgress(0);
2651             activeTextEl.dom.value = opt.value || "";
2652             if(opt.prompt){
2653                 dlg.setDefaultButton(activeTextEl);
2654             }else{
2655                 var bs = opt.buttons;
2656                 var db = null;
2657                 if(bs && bs.ok){
2658                     db = buttons["ok"];
2659                 }else if(bs && bs.yes){
2660                     db = buttons["yes"];
2661                 }
2662                 dlg.setDefaultButton(db);
2663             }
2664             bwidth = updateButtons(opt.buttons);
2665             this.updateText(opt.msg);
2666             if(opt.cls){
2667                 d.el.addClass(opt.cls);
2668             }
2669             d.proxyDrag = opt.proxyDrag === true;
2670             d.modal = opt.modal !== false;
2671             d.mask = opt.modal !== false ? mask : false;
2672             if(!d.isVisible()){
2673                 // force it to the end of the z-index stack so it gets a cursor in FF
2674                 document.body.appendChild(dlg.el.dom);
2675                 d.animateTarget = null;
2676                 d.show(options.animEl);
2677             }
2678             return this;
2679         },
2680
2681         /**
2682          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2683          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2684          * and closing the message box when the process is complete.
2685          * @param {String} title The title bar text
2686          * @param {String} msg The message box body text
2687          * @return {Roo.MessageBox} This message box
2688          */
2689         progress : function(title, msg){
2690             this.show({
2691                 title : title,
2692                 msg : msg,
2693                 buttons: false,
2694                 progress:true,
2695                 closable:false,
2696                 minWidth: this.minProgressWidth,
2697                 modal : true
2698             });
2699             return this;
2700         },
2701
2702         /**
2703          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2704          * If a callback function is passed it will be called after the user clicks the button, and the
2705          * id of the button that was clicked will be passed as the only parameter to the callback
2706          * (could also be the top-right close button).
2707          * @param {String} title The title bar text
2708          * @param {String} msg The message box body text
2709          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2710          * @param {Object} scope (optional) The scope of the callback function
2711          * @return {Roo.MessageBox} This message box
2712          */
2713         alert : function(title, msg, fn, scope){
2714             this.show({
2715                 title : title,
2716                 msg : msg,
2717                 buttons: this.OK,
2718                 fn: fn,
2719                 scope : scope,
2720                 modal : true
2721             });
2722             return this;
2723         },
2724
2725         /**
2726          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2727          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2728          * You are responsible for closing the message box when the process is complete.
2729          * @param {String} msg The message box body text
2730          * @param {String} title (optional) The title bar text
2731          * @return {Roo.MessageBox} This message box
2732          */
2733         wait : function(msg, title){
2734             this.show({
2735                 title : title,
2736                 msg : msg,
2737                 buttons: false,
2738                 closable:false,
2739                 progress:true,
2740                 modal:true,
2741                 width:300,
2742                 wait:true
2743             });
2744             waitTimer = Roo.TaskMgr.start({
2745                 run: function(i){
2746                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2747                 },
2748                 interval: 1000
2749             });
2750             return this;
2751         },
2752
2753         /**
2754          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2755          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2756          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2757          * @param {String} title The title bar text
2758          * @param {String} msg The message box body text
2759          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2760          * @param {Object} scope (optional) The scope of the callback function
2761          * @return {Roo.MessageBox} This message box
2762          */
2763         confirm : function(title, msg, fn, scope){
2764             this.show({
2765                 title : title,
2766                 msg : msg,
2767                 buttons: this.YESNO,
2768                 fn: fn,
2769                 scope : scope,
2770                 modal : true
2771             });
2772             return this;
2773         },
2774
2775         /**
2776          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2777          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2778          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2779          * (could also be the top-right close button) and the text that was entered will be passed as the two
2780          * parameters to the callback.
2781          * @param {String} title The title bar text
2782          * @param {String} msg The message box body text
2783          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2784          * @param {Object} scope (optional) The scope of the callback function
2785          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2786          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2787          * @return {Roo.MessageBox} This message box
2788          */
2789         prompt : function(title, msg, fn, scope, multiline){
2790             this.show({
2791                 title : title,
2792                 msg : msg,
2793                 buttons: this.OKCANCEL,
2794                 fn: fn,
2795                 minWidth:250,
2796                 scope : scope,
2797                 prompt:true,
2798                 multiline: multiline,
2799                 modal : true
2800             });
2801             return this;
2802         },
2803
2804         /**
2805          * Button config that displays a single OK button
2806          * @type Object
2807          */
2808         OK : {ok:true},
2809         /**
2810          * Button config that displays Yes and No buttons
2811          * @type Object
2812          */
2813         YESNO : {yes:true, no:true},
2814         /**
2815          * Button config that displays OK and Cancel buttons
2816          * @type Object
2817          */
2818         OKCANCEL : {ok:true, cancel:true},
2819         /**
2820          * Button config that displays Yes, No and Cancel buttons
2821          * @type Object
2822          */
2823         YESNOCANCEL : {yes:true, no:true, cancel:true},
2824
2825         /**
2826          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2827          * @type Number
2828          */
2829         defaultTextHeight : 75,
2830         /**
2831          * The maximum width in pixels of the message box (defaults to 600)
2832          * @type Number
2833          */
2834         maxWidth : 600,
2835         /**
2836          * The minimum width in pixels of the message box (defaults to 100)
2837          * @type Number
2838          */
2839         minWidth : 100,
2840         /**
2841          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2842          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2843          * @type Number
2844          */
2845         minProgressWidth : 250,
2846         /**
2847          * An object containing the default button text strings that can be overriden for localized language support.
2848          * Supported properties are: ok, cancel, yes and no.
2849          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2850          * @type Object
2851          */
2852         buttonText : {
2853             ok : "OK",
2854             cancel : "Cancel",
2855             yes : "Yes",
2856             no : "No"
2857         }
2858     };
2859 }();
2860
2861 /**
2862  * Shorthand for {@link Roo.MessageBox}
2863  */
2864 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2865 Roo.Msg = Roo.Msg || Roo.MessageBox;
2866 /*
2867  * - LGPL
2868  *
2869  * navbar
2870  * 
2871  */
2872
2873 /**
2874  * @class Roo.bootstrap.Navbar
2875  * @extends Roo.bootstrap.Component
2876  * Bootstrap Navbar class
2877
2878  * @constructor
2879  * Create a new Navbar
2880  * @param {Object} config The config object
2881  */
2882
2883
2884 Roo.bootstrap.Navbar = function(config){
2885     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2886     
2887 };
2888
2889 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2890     
2891     
2892    
2893     // private
2894     navItems : false,
2895     loadMask : false,
2896     
2897     
2898     getAutoCreate : function(){
2899         
2900         
2901         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2902         
2903     },
2904     
2905     initEvents :function ()
2906     {
2907         //Roo.log(this.el.select('.navbar-toggle',true));
2908         this.el.select('.navbar-toggle',true).on('click', function() {
2909            // Roo.log('click');
2910             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2911         }, this);
2912         
2913         var mark = {
2914             tag: "div",
2915             cls:"x-dlg-mask"
2916         }
2917         
2918         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2919         
2920         var size = this.el.getSize();
2921         this.maskEl.setSize(size.width, size.height);
2922         this.maskEl.enableDisplayMode("block");
2923         this.maskEl.hide();
2924         
2925         if(this.loadMask){
2926             this.maskEl.show();
2927         }
2928     },
2929     
2930     
2931     getChildContainer : function()
2932     {
2933         if (this.el.select('.collapse').getCount()) {
2934             return this.el.select('.collapse',true).first();
2935         }
2936         
2937         return this.el;
2938     },
2939     
2940     mask : function()
2941     {
2942         this.maskEl.show();
2943     },
2944     
2945     unmask : function()
2946     {
2947         this.maskEl.hide();
2948     } 
2949     
2950     
2951     
2952     
2953 });
2954
2955
2956
2957  
2958
2959  /*
2960  * - LGPL
2961  *
2962  * navbar
2963  * 
2964  */
2965
2966 /**
2967  * @class Roo.bootstrap.NavSimplebar
2968  * @extends Roo.bootstrap.Navbar
2969  * Bootstrap Sidebar class
2970  *
2971  * @cfg {Boolean} inverse is inverted color
2972  * 
2973  * @cfg {String} type (nav | pills | tabs)
2974  * @cfg {Boolean} arrangement stacked | justified
2975  * @cfg {String} align (left | right) alignment
2976  * 
2977  * @cfg {Boolean} main (true|false) main nav bar? default false
2978  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2979  * 
2980  * @cfg {String} tag (header|footer|nav|div) default is nav 
2981
2982  * 
2983  * 
2984  * 
2985  * @constructor
2986  * Create a new Sidebar
2987  * @param {Object} config The config object
2988  */
2989
2990
2991 Roo.bootstrap.NavSimplebar = function(config){
2992     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2993 };
2994
2995 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2996     
2997     inverse: false,
2998     
2999     type: false,
3000     arrangement: '',
3001     align : false,
3002     
3003     
3004     
3005     main : false,
3006     
3007     
3008     tag : false,
3009     
3010     
3011     getAutoCreate : function(){
3012         
3013         
3014         var cfg = {
3015             tag : this.tag || 'div',
3016             cls : 'navbar'
3017         };
3018           
3019         
3020         cfg.cn = [
3021             {
3022                 cls: 'nav',
3023                 tag : 'ul'
3024             }
3025         ];
3026         
3027          
3028         this.type = this.type || 'nav';
3029         if (['tabs','pills'].indexOf(this.type)!==-1) {
3030             cfg.cn[0].cls += ' nav-' + this.type
3031         
3032         
3033         } else {
3034             if (this.type!=='nav') {
3035                 Roo.log('nav type must be nav/tabs/pills')
3036             }
3037             cfg.cn[0].cls += ' navbar-nav'
3038         }
3039         
3040         
3041         
3042         
3043         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3044             cfg.cn[0].cls += ' nav-' + this.arrangement;
3045         }
3046         
3047         
3048         if (this.align === 'right') {
3049             cfg.cn[0].cls += ' navbar-right';
3050         }
3051         
3052         if (this.inverse) {
3053             cfg.cls += ' navbar-inverse';
3054             
3055         }
3056         
3057         
3058         return cfg;
3059     
3060         
3061     }
3062     
3063     
3064     
3065 });
3066
3067
3068
3069  
3070
3071  
3072        /*
3073  * - LGPL
3074  *
3075  * navbar
3076  * 
3077  */
3078
3079 /**
3080  * @class Roo.bootstrap.NavHeaderbar
3081  * @extends Roo.bootstrap.NavSimplebar
3082  * Bootstrap Sidebar class
3083  *
3084  * @cfg {String} brand what is brand
3085  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3086  * @cfg {String} brand_href href of the brand
3087  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3088  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3089  * 
3090  * @constructor
3091  * Create a new Sidebar
3092  * @param {Object} config The config object
3093  */
3094
3095
3096 Roo.bootstrap.NavHeaderbar = function(config){
3097     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3098 };
3099
3100 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3101     
3102     position: '',
3103     brand: '',
3104     brand_href: false,
3105     srButton : true,
3106     autohide : false,
3107     
3108     getAutoCreate : function(){
3109         
3110         var   cfg = {
3111             tag: this.nav || 'nav',
3112             cls: 'navbar',
3113             role: 'navigation',
3114             cn: []
3115         };
3116         
3117         if(this.srButton){
3118             cfg.cn.push({
3119                 tag: 'div',
3120                 cls: 'navbar-header',
3121                 cn: [
3122                     {
3123                         tag: 'button',
3124                         type: 'button',
3125                         cls: 'navbar-toggle',
3126                         'data-toggle': 'collapse',
3127                         cn: [
3128                             {
3129                                 tag: 'span',
3130                                 cls: 'sr-only',
3131                                 html: 'Toggle navigation'
3132                             },
3133                             {
3134                                 tag: 'span',
3135                                 cls: 'icon-bar'
3136                             },
3137                             {
3138                                 tag: 'span',
3139                                 cls: 'icon-bar'
3140                             },
3141                             {
3142                                 tag: 'span',
3143                                 cls: 'icon-bar'
3144                             }
3145                         ]
3146                     }
3147                 ]
3148             });
3149         }
3150         
3151         cfg.cn.push({
3152             tag: 'div',
3153             cls: 'collapse navbar-collapse',
3154             cn : []
3155         });
3156         
3157         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3158         
3159         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3160             cfg.cls += ' navbar-' + this.position;
3161             
3162             // tag can override this..
3163             
3164             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3165         }
3166         
3167         if (this.brand !== '') {
3168             cfg.cn[0].cn.push({
3169                 tag: 'a',
3170                 href: this.brand_href ? this.brand_href : '#',
3171                 cls: 'navbar-brand',
3172                 cn: [
3173                 this.brand
3174                 ]
3175             });
3176         }
3177         
3178         if(this.main){
3179             cfg.cls += ' main-nav';
3180         }
3181         
3182         
3183         return cfg;
3184
3185         
3186     },
3187     initEvents : function()
3188     {
3189         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3190         
3191         if (this.autohide) {
3192             
3193             var prevScroll = 0;
3194             var ft = this.el;
3195             
3196             Roo.get(document).on('scroll',function(e) {
3197                 var ns = Roo.get(document).getScroll().top;
3198                 var os = prevScroll;
3199                 prevScroll = ns;
3200                 
3201                 if(ns > os){
3202                     ft.removeClass('slideDown');
3203                     ft.addClass('slideUp');
3204                     return;
3205                 }
3206                 ft.removeClass('slideUp');
3207                 ft.addClass('slideDown');
3208                  
3209               
3210           },this);
3211         }
3212     }    
3213           
3214       
3215     
3216     
3217 });
3218
3219
3220
3221  
3222
3223  /*
3224  * - LGPL
3225  *
3226  * navbar
3227  * 
3228  */
3229
3230 /**
3231  * @class Roo.bootstrap.NavSidebar
3232  * @extends Roo.bootstrap.Navbar
3233  * Bootstrap Sidebar class
3234  * 
3235  * @constructor
3236  * Create a new Sidebar
3237  * @param {Object} config The config object
3238  */
3239
3240
3241 Roo.bootstrap.NavSidebar = function(config){
3242     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3243 };
3244
3245 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3246     
3247     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3248     
3249     getAutoCreate : function(){
3250         
3251         
3252         return  {
3253             tag: 'div',
3254             cls: 'sidebar sidebar-nav'
3255         };
3256     
3257         
3258     }
3259     
3260     
3261     
3262 });
3263
3264
3265
3266  
3267
3268  /*
3269  * - LGPL
3270  *
3271  * nav group
3272  * 
3273  */
3274
3275 /**
3276  * @class Roo.bootstrap.NavGroup
3277  * @extends Roo.bootstrap.Component
3278  * Bootstrap NavGroup class
3279  * @cfg {String} align left | right
3280  * @cfg {Boolean} inverse false | true
3281  * @cfg {String} type (nav|pills|tab) default nav
3282  * @cfg {String} navId - reference Id for navbar.
3283
3284  * 
3285  * @constructor
3286  * Create a new nav group
3287  * @param {Object} config The config object
3288  */
3289
3290 Roo.bootstrap.NavGroup = function(config){
3291     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3292     this.navItems = [];
3293    
3294     Roo.bootstrap.NavGroup.register(this);
3295      this.addEvents({
3296         /**
3297              * @event changed
3298              * Fires when the active item changes
3299              * @param {Roo.bootstrap.NavGroup} this
3300              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3301              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3302          */
3303         'changed': true
3304      });
3305     
3306 };
3307
3308 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3309     
3310     align: '',
3311     inverse: false,
3312     form: false,
3313     type: 'nav',
3314     navId : '',
3315     // private
3316     
3317     navItems : false, 
3318     
3319     getAutoCreate : function()
3320     {
3321         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3322         
3323         cfg = {
3324             tag : 'ul',
3325             cls: 'nav' 
3326         }
3327         
3328         if (['tabs','pills'].indexOf(this.type)!==-1) {
3329             cfg.cls += ' nav-' + this.type
3330         } else {
3331             if (this.type!=='nav') {
3332                 Roo.log('nav type must be nav/tabs/pills')
3333             }
3334             cfg.cls += ' navbar-nav'
3335         }
3336         
3337         if (this.parent().sidebar) {
3338             cfg = {
3339                 tag: 'ul',
3340                 cls: 'dashboard-menu sidebar-menu'
3341             }
3342             
3343             return cfg;
3344         }
3345         
3346         if (this.form === true) {
3347             cfg = {
3348                 tag: 'form',
3349                 cls: 'navbar-form'
3350             }
3351             
3352             if (this.align === 'right') {
3353                 cfg.cls += ' navbar-right';
3354             } else {
3355                 cfg.cls += ' navbar-left';
3356             }
3357         }
3358         
3359         if (this.align === 'right') {
3360             cfg.cls += ' navbar-right';
3361         }
3362         
3363         if (this.inverse) {
3364             cfg.cls += ' navbar-inverse';
3365             
3366         }
3367         
3368         
3369         return cfg;
3370     },
3371     /**
3372     * sets the active Navigation item
3373     * @param {Roo.bootstrap.NavItem} the new current navitem
3374     */
3375     setActiveItem : function(item)
3376     {
3377         var prev = false;
3378         Roo.each(this.navItems, function(v){
3379             if (v == item) {
3380                 return ;
3381             }
3382             if (v.isActive()) {
3383                 v.setActive(false, true);
3384                 prev = v;
3385                 
3386             }
3387             
3388         });
3389
3390         item.setActive(true, true);
3391         this.fireEvent('changed', this, item, prev);
3392         
3393         
3394     },
3395     /**
3396     * gets the active Navigation item
3397     * @return {Roo.bootstrap.NavItem} the current navitem
3398     */
3399     getActive : function()
3400     {
3401         
3402         var prev = false;
3403         Roo.each(this.navItems, function(v){
3404             
3405             if (v.isActive()) {
3406                 prev = v;
3407                 
3408             }
3409             
3410         });
3411         return prev;
3412     },
3413     
3414     indexOfNav : function()
3415     {
3416         
3417         var prev = false;
3418         Roo.each(this.navItems, function(v,i){
3419             
3420             if (v.isActive()) {
3421                 prev = i;
3422                 
3423             }
3424             
3425         });
3426         return prev;
3427     },
3428     /**
3429     * adds a Navigation item
3430     * @param {Roo.bootstrap.NavItem} the navitem to add
3431     */
3432     addItem : function(cfg)
3433     {
3434         var cn = new Roo.bootstrap.NavItem(cfg);
3435         this.register(cn);
3436         cn.parentId = this.id;
3437         cn.onRender(this.el, null);
3438         return cn;
3439     },
3440     /**
3441     * register a Navigation item
3442     * @param {Roo.bootstrap.NavItem} the navitem to add
3443     */
3444     register : function(item)
3445     {
3446         this.navItems.push( item);
3447         item.navId = this.navId;
3448     
3449     },
3450   
3451     
3452     getNavItem: function(tabId)
3453     {
3454         var ret = false;
3455         Roo.each(this.navItems, function(e) {
3456             if (e.tabId == tabId) {
3457                ret =  e;
3458                return false;
3459             }
3460             return true;
3461             
3462         });
3463         return ret;
3464     },
3465     
3466     setActiveNext : function()
3467     {
3468         var i = this.indexOfNav(this.getActive());
3469         if (i > this.navItems.length) {
3470             return;
3471         }
3472         this.setActiveItem(this.navItems[i+1]);
3473     },
3474     setActivePrev : function()
3475     {
3476         var i = this.indexOfNav(this.getActive());
3477         if (i  < 1) {
3478             return;
3479         }
3480         this.setActiveItem(this.navItems[i-1]);
3481     },
3482     clearWasActive : function(except) {
3483         Roo.each(this.navItems, function(e) {
3484             if (e.tabId != except.tabId && e.was_active) {
3485                e.was_active = false;
3486                return false;
3487             }
3488             return true;
3489             
3490         });
3491     },
3492     getWasActive : function ()
3493     {
3494         var r = false;
3495         Roo.each(this.navItems, function(e) {
3496             if (e.was_active) {
3497                r = e;
3498                return false;
3499             }
3500             return true;
3501             
3502         });
3503         return r;
3504     }
3505     
3506     
3507 });
3508
3509  
3510 Roo.apply(Roo.bootstrap.NavGroup, {
3511     
3512     groups: {},
3513      /**
3514     * register a Navigation Group
3515     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3516     */
3517     register : function(navgrp)
3518     {
3519         this.groups[navgrp.navId] = navgrp;
3520         
3521     },
3522     /**
3523     * fetch a Navigation Group based on the navigation ID
3524     * @param {string} the navgroup to add
3525     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3526     */
3527     get: function(navId) {
3528         if (typeof(this.groups[navId]) == 'undefined') {
3529             return false;
3530             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3531         }
3532         return this.groups[navId] ;
3533     }
3534     
3535     
3536     
3537 });
3538
3539  /*
3540  * - LGPL
3541  *
3542  * row
3543  * 
3544  */
3545
3546 /**
3547  * @class Roo.bootstrap.NavItem
3548  * @extends Roo.bootstrap.Component
3549  * Bootstrap Navbar.NavItem class
3550  * @cfg {String} href  link to
3551  * @cfg {String} html content of button
3552  * @cfg {String} badge text inside badge
3553  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3554  * @cfg {String} glyphicon name of glyphicon
3555  * @cfg {String} icon name of font awesome icon
3556  * @cfg {Boolean} active Is item active
3557  * @cfg {Boolean} disabled Is item disabled
3558  
3559  * @cfg {Boolean} preventDefault (true | false) default false
3560  * @cfg {String} tabId the tab that this item activates.
3561  * @cfg {String} tagtype (a|span) render as a href or span?
3562   
3563  * @constructor
3564  * Create a new Navbar Item
3565  * @param {Object} config The config object
3566  */
3567 Roo.bootstrap.NavItem = function(config){
3568     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3569     this.addEvents({
3570         // raw events
3571         /**
3572          * @event click
3573          * The raw click event for the entire grid.
3574          * @param {Roo.EventObject} e
3575          */
3576         "click" : true,
3577          /**
3578             * @event changed
3579             * Fires when the active item active state changes
3580             * @param {Roo.bootstrap.NavItem} this
3581             * @param {boolean} state the new state
3582              
3583          */
3584         'changed': true
3585     });
3586    
3587 };
3588
3589 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3590     
3591     href: false,
3592     html: '',
3593     badge: '',
3594     icon: false,
3595     glyphicon: false,
3596     active: false,
3597     preventDefault : false,
3598     tabId : false,
3599     tagtype : 'a',
3600     disabled : false,
3601     
3602     was_active : false,
3603     
3604     getAutoCreate : function(){
3605          
3606         var cfg = {
3607             tag: 'li',
3608             cls: 'nav-item'
3609             
3610         }
3611         if (this.active) {
3612             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3613         }
3614         if (this.disabled) {
3615             cfg.cls += ' disabled';
3616         }
3617         
3618         if (this.href || this.html || this.glyphicon || this.icon) {
3619             cfg.cn = [
3620                 {
3621                     tag: this.tagtype,
3622                     href : this.href || "#",
3623                     html: this.html || ''
3624                 }
3625             ];
3626             
3627             if (this.icon) {
3628                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3629             }
3630
3631             if(this.glyphicon) {
3632                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3633             }
3634             
3635             if (this.menu) {
3636                 
3637                 cfg.cn[0].html += " <span class='caret'></span>";
3638              
3639             }
3640             
3641             if (this.badge !== '') {
3642                  
3643                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3644             }
3645         }
3646         
3647         
3648         
3649         return cfg;
3650     },
3651     initEvents: function() {
3652        // Roo.log('init events?');
3653        // Roo.log(this.el.dom);
3654         if (typeof (this.menu) != 'undefined') {
3655             this.menu.parentType = this.xtype;
3656             this.menu.triggerEl = this.el;
3657             this.addxtype(Roo.apply({}, this.menu));
3658         }
3659
3660        
3661         this.el.select('a',true).on('click', this.onClick, this);
3662         // at this point parent should be available..
3663         this.parent().register(this);
3664     },
3665     
3666     onClick : function(e)
3667     {
3668          
3669         if(this.preventDefault){
3670             e.preventDefault();
3671         }
3672         if (this.disabled) {
3673             return;
3674         }
3675         
3676         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3677         if (tg && tg.transition) {
3678             Roo.log("waiting for the transitionend");
3679             return;
3680         }
3681         
3682         Roo.log("fire event clicked");
3683         if(this.fireEvent('click', this, e) === false){
3684             return;
3685         };
3686         
3687         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3688             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3689                 this.parent().setActiveItem(this);
3690             }
3691         } 
3692     },
3693     
3694     isActive: function () {
3695         return this.active
3696     },
3697     setActive : function(state, fire, is_was_active)
3698     {
3699         if (this.active && !state & this.navId) {
3700             this.was_active = true;
3701             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3702             if (nv) {
3703                 nv.clearWasActive(this);
3704             }
3705             
3706         }
3707         this.active = state;
3708         
3709         if (!state ) {
3710             this.el.removeClass('active');
3711         } else if (!this.el.hasClass('active')) {
3712             this.el.addClass('active');
3713         }
3714         if (fire) {
3715             this.fireEvent('changed', this, state);
3716         }
3717         
3718         // show a panel if it's registered and related..
3719         
3720         if (!this.navId || !this.tabId || !state || is_was_active) {
3721             return;
3722         }
3723         
3724         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3725         if (!tg) {
3726             return;
3727         }
3728         var pan = tg.getPanelByName(this.tabId);
3729         if (!pan) {
3730             return;
3731         }
3732         // if we can not flip to new panel - go back to old nav highlight..
3733         if (false == tg.showPanel(pan)) {
3734             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3735             if (nv) {
3736                 var onav = nv.getWasActive();
3737                 if (onav) {
3738                     onav.setActive(true, false, true);
3739                 }
3740             }
3741             
3742         }
3743         
3744         
3745         
3746     },
3747      // this should not be here...
3748     setDisabled : function(state)
3749     {
3750         this.disabled = state;
3751         if (!state ) {
3752             this.el.removeClass('disabled');
3753         } else if (!this.el.hasClass('disabled')) {
3754             this.el.addClass('disabled');
3755         }
3756         
3757     }
3758 });
3759  
3760
3761  /*
3762  * - LGPL
3763  *
3764  * sidebar item
3765  *
3766  *  li
3767  *    <span> icon </span>
3768  *    <span> text </span>
3769  *    <span>badge </span>
3770  */
3771
3772 /**
3773  * @class Roo.bootstrap.NavSidebarItem
3774  * @extends Roo.bootstrap.NavItem
3775  * Bootstrap Navbar.NavSidebarItem class
3776  * @constructor
3777  * Create a new Navbar Button
3778  * @param {Object} config The config object
3779  */
3780 Roo.bootstrap.NavSidebarItem = function(config){
3781     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3782     this.addEvents({
3783         // raw events
3784         /**
3785          * @event click
3786          * The raw click event for the entire grid.
3787          * @param {Roo.EventObject} e
3788          */
3789         "click" : true,
3790          /**
3791             * @event changed
3792             * Fires when the active item active state changes
3793             * @param {Roo.bootstrap.NavSidebarItem} this
3794             * @param {boolean} state the new state
3795              
3796          */
3797         'changed': true
3798     });
3799    
3800 };
3801
3802 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3803     
3804     
3805     getAutoCreate : function(){
3806         
3807         
3808         var a = {
3809                 tag: 'a',
3810                 href : this.href || '#',
3811                 cls: '',
3812                 html : '',
3813                 cn : []
3814         };
3815         var cfg = {
3816             tag: 'li',
3817             cls: '',
3818             cn: [ a ]
3819         }
3820         var span = {
3821             tag: 'span',
3822             html : this.html || ''
3823         }
3824         
3825         
3826         if (this.active) {
3827             cfg.cls += ' active';
3828         }
3829         
3830         // left icon..
3831         if (this.glyphicon || this.icon) {
3832             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3833             a.cn.push({ tag : 'i', cls : c }) ;
3834         }
3835         // html..
3836         a.cn.push(span);
3837         // then badge..
3838         if (this.badge !== '') {
3839             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3840         }
3841         // fi
3842         if (this.menu) {
3843             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3844             a.cls += 'dropdown-toggle treeview' ;
3845             
3846         }
3847         
3848         
3849         
3850         return cfg;
3851          
3852            
3853     }
3854    
3855      
3856  
3857 });
3858  
3859
3860  /*
3861  * - LGPL
3862  *
3863  * row
3864  * 
3865  */
3866
3867 /**
3868  * @class Roo.bootstrap.Row
3869  * @extends Roo.bootstrap.Component
3870  * Bootstrap Row class (contains columns...)
3871  * 
3872  * @constructor
3873  * Create a new Row
3874  * @param {Object} config The config object
3875  */
3876
3877 Roo.bootstrap.Row = function(config){
3878     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3879 };
3880
3881 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3882     
3883     getAutoCreate : function(){
3884        return {
3885             cls: 'row clearfix'
3886        };
3887     }
3888     
3889     
3890 });
3891
3892  
3893
3894  /*
3895  * - LGPL
3896  *
3897  * element
3898  * 
3899  */
3900
3901 /**
3902  * @class Roo.bootstrap.Element
3903  * @extends Roo.bootstrap.Component
3904  * Bootstrap Element class
3905  * @cfg {String} html contents of the element
3906  * @cfg {String} tag tag of the element
3907  * @cfg {String} cls class of the element
3908  * 
3909  * @constructor
3910  * Create a new Element
3911  * @param {Object} config The config object
3912  */
3913
3914 Roo.bootstrap.Element = function(config){
3915     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3916 };
3917
3918 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3919     
3920     tag: 'div',
3921     cls: '',
3922     html: '',
3923      
3924     
3925     getAutoCreate : function(){
3926         
3927         var cfg = {
3928             tag: this.tag,
3929             cls: this.cls,
3930             html: this.html
3931         }
3932         
3933         
3934         
3935         return cfg;
3936     }
3937    
3938 });
3939
3940  
3941
3942  /*
3943  * - LGPL
3944  *
3945  * pagination
3946  * 
3947  */
3948
3949 /**
3950  * @class Roo.bootstrap.Pagination
3951  * @extends Roo.bootstrap.Component
3952  * Bootstrap Pagination class
3953  * @cfg {String} size xs | sm | md | lg
3954  * @cfg {Boolean} inverse false | true
3955  * 
3956  * @constructor
3957  * Create a new Pagination
3958  * @param {Object} config The config object
3959  */
3960
3961 Roo.bootstrap.Pagination = function(config){
3962     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3963 };
3964
3965 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3966     
3967     cls: false,
3968     size: false,
3969     inverse: false,
3970     
3971     getAutoCreate : function(){
3972         var cfg = {
3973             tag: 'ul',
3974                 cls: 'pagination'
3975         };
3976         if (this.inverse) {
3977             cfg.cls += ' inverse';
3978         }
3979         if (this.html) {
3980             cfg.html=this.html;
3981         }
3982         if (this.cls) {
3983             cfg.cls += " " + this.cls;
3984         }
3985         return cfg;
3986     }
3987    
3988 });
3989
3990  
3991
3992  /*
3993  * - LGPL
3994  *
3995  * Pagination item
3996  * 
3997  */
3998
3999
4000 /**
4001  * @class Roo.bootstrap.PaginationItem
4002  * @extends Roo.bootstrap.Component
4003  * Bootstrap PaginationItem class
4004  * @cfg {String} html text
4005  * @cfg {String} href the link
4006  * @cfg {Boolean} preventDefault (true | false) default true
4007  * @cfg {Boolean} active (true | false) default false
4008  * 
4009  * 
4010  * @constructor
4011  * Create a new PaginationItem
4012  * @param {Object} config The config object
4013  */
4014
4015
4016 Roo.bootstrap.PaginationItem = function(config){
4017     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4018     this.addEvents({
4019         // raw events
4020         /**
4021          * @event click
4022          * The raw click event for the entire grid.
4023          * @param {Roo.EventObject} e
4024          */
4025         "click" : true
4026     });
4027 };
4028
4029 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4030     
4031     href : false,
4032     html : false,
4033     preventDefault: true,
4034     active : false,
4035     cls : false,
4036     
4037     getAutoCreate : function(){
4038         var cfg= {
4039             tag: 'li',
4040             cn: [
4041                 {
4042                     tag : 'a',
4043                     href : this.href ? this.href : '#',
4044                     html : this.html ? this.html : ''
4045                 }
4046             ]
4047         };
4048         
4049         if(this.cls){
4050             cfg.cls = this.cls;
4051         }
4052         
4053         if(this.active){
4054             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4055         }
4056         
4057         return cfg;
4058     },
4059     
4060     initEvents: function() {
4061         
4062         this.el.on('click', this.onClick, this);
4063         
4064     },
4065     onClick : function(e)
4066     {
4067         Roo.log('PaginationItem on click ');
4068         if(this.preventDefault){
4069             e.preventDefault();
4070         }
4071         
4072         this.fireEvent('click', this, e);
4073     }
4074    
4075 });
4076
4077  
4078
4079  /*
4080  * - LGPL
4081  *
4082  * slider
4083  * 
4084  */
4085
4086
4087 /**
4088  * @class Roo.bootstrap.Slider
4089  * @extends Roo.bootstrap.Component
4090  * Bootstrap Slider class
4091  *    
4092  * @constructor
4093  * Create a new Slider
4094  * @param {Object} config The config object
4095  */
4096
4097 Roo.bootstrap.Slider = function(config){
4098     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4099 };
4100
4101 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4102     
4103     getAutoCreate : function(){
4104         
4105         var cfg = {
4106             tag: 'div',
4107             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4108             cn: [
4109                 {
4110                     tag: 'a',
4111                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4112                 }
4113             ]
4114         }
4115         
4116         return cfg;
4117     }
4118    
4119 });
4120
4121  /*
4122  * Based on:
4123  * Ext JS Library 1.1.1
4124  * Copyright(c) 2006-2007, Ext JS, LLC.
4125  *
4126  * Originally Released Under LGPL - original licence link has changed is not relivant.
4127  *
4128  * Fork - LGPL
4129  * <script type="text/javascript">
4130  */
4131  
4132
4133 /**
4134  * @class Roo.grid.ColumnModel
4135  * @extends Roo.util.Observable
4136  * This is the default implementation of a ColumnModel used by the Grid. It defines
4137  * the columns in the grid.
4138  * <br>Usage:<br>
4139  <pre><code>
4140  var colModel = new Roo.grid.ColumnModel([
4141         {header: "Ticker", width: 60, sortable: true, locked: true},
4142         {header: "Company Name", width: 150, sortable: true},
4143         {header: "Market Cap.", width: 100, sortable: true},
4144         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4145         {header: "Employees", width: 100, sortable: true, resizable: false}
4146  ]);
4147  </code></pre>
4148  * <p>
4149  
4150  * The config options listed for this class are options which may appear in each
4151  * individual column definition.
4152  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4153  * @constructor
4154  * @param {Object} config An Array of column config objects. See this class's
4155  * config objects for details.
4156 */
4157 Roo.grid.ColumnModel = function(config){
4158         /**
4159      * The config passed into the constructor
4160      */
4161     this.config = config;
4162     this.lookup = {};
4163
4164     // if no id, create one
4165     // if the column does not have a dataIndex mapping,
4166     // map it to the order it is in the config
4167     for(var i = 0, len = config.length; i < len; i++){
4168         var c = config[i];
4169         if(typeof c.dataIndex == "undefined"){
4170             c.dataIndex = i;
4171         }
4172         if(typeof c.renderer == "string"){
4173             c.renderer = Roo.util.Format[c.renderer];
4174         }
4175         if(typeof c.id == "undefined"){
4176             c.id = Roo.id();
4177         }
4178         if(c.editor && c.editor.xtype){
4179             c.editor  = Roo.factory(c.editor, Roo.grid);
4180         }
4181         if(c.editor && c.editor.isFormField){
4182             c.editor = new Roo.grid.GridEditor(c.editor);
4183         }
4184         this.lookup[c.id] = c;
4185     }
4186
4187     /**
4188      * The width of columns which have no width specified (defaults to 100)
4189      * @type Number
4190      */
4191     this.defaultWidth = 100;
4192
4193     /**
4194      * Default sortable of columns which have no sortable specified (defaults to false)
4195      * @type Boolean
4196      */
4197     this.defaultSortable = false;
4198
4199     this.addEvents({
4200         /**
4201              * @event widthchange
4202              * Fires when the width of a column changes.
4203              * @param {ColumnModel} this
4204              * @param {Number} columnIndex The column index
4205              * @param {Number} newWidth The new width
4206              */
4207             "widthchange": true,
4208         /**
4209              * @event headerchange
4210              * Fires when the text of a header changes.
4211              * @param {ColumnModel} this
4212              * @param {Number} columnIndex The column index
4213              * @param {Number} newText The new header text
4214              */
4215             "headerchange": true,
4216         /**
4217              * @event hiddenchange
4218              * Fires when a column is hidden or "unhidden".
4219              * @param {ColumnModel} this
4220              * @param {Number} columnIndex The column index
4221              * @param {Boolean} hidden true if hidden, false otherwise
4222              */
4223             "hiddenchange": true,
4224             /**
4225          * @event columnmoved
4226          * Fires when a column is moved.
4227          * @param {ColumnModel} this
4228          * @param {Number} oldIndex
4229          * @param {Number} newIndex
4230          */
4231         "columnmoved" : true,
4232         /**
4233          * @event columlockchange
4234          * Fires when a column's locked state is changed
4235          * @param {ColumnModel} this
4236          * @param {Number} colIndex
4237          * @param {Boolean} locked true if locked
4238          */
4239         "columnlockchange" : true
4240     });
4241     Roo.grid.ColumnModel.superclass.constructor.call(this);
4242 };
4243 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4244     /**
4245      * @cfg {String} header The header text to display in the Grid view.
4246      */
4247     /**
4248      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4249      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4250      * specified, the column's index is used as an index into the Record's data Array.
4251      */
4252     /**
4253      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4254      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4255      */
4256     /**
4257      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4258      * Defaults to the value of the {@link #defaultSortable} property.
4259      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4260      */
4261     /**
4262      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4263      */
4264     /**
4265      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4266      */
4267     /**
4268      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4269      */
4270     /**
4271      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4272      */
4273     /**
4274      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4275      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4276      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4277      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4278      */
4279        /**
4280      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4281      */
4282     /**
4283      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4284      */
4285
4286     /**
4287      * Returns the id of the column at the specified index.
4288      * @param {Number} index The column index
4289      * @return {String} the id
4290      */
4291     getColumnId : function(index){
4292         return this.config[index].id;
4293     },
4294
4295     /**
4296      * Returns the column for a specified id.
4297      * @param {String} id The column id
4298      * @return {Object} the column
4299      */
4300     getColumnById : function(id){
4301         return this.lookup[id];
4302     },
4303
4304     
4305     /**
4306      * Returns the column for a specified dataIndex.
4307      * @param {String} dataIndex The column dataIndex
4308      * @return {Object|Boolean} the column or false if not found
4309      */
4310     getColumnByDataIndex: function(dataIndex){
4311         var index = this.findColumnIndex(dataIndex);
4312         return index > -1 ? this.config[index] : false;
4313     },
4314     
4315     /**
4316      * Returns the index for a specified column id.
4317      * @param {String} id The column id
4318      * @return {Number} the index, or -1 if not found
4319      */
4320     getIndexById : function(id){
4321         for(var i = 0, len = this.config.length; i < len; i++){
4322             if(this.config[i].id == id){
4323                 return i;
4324             }
4325         }
4326         return -1;
4327     },
4328     
4329     /**
4330      * Returns the index for a specified column dataIndex.
4331      * @param {String} dataIndex The column dataIndex
4332      * @return {Number} the index, or -1 if not found
4333      */
4334     
4335     findColumnIndex : function(dataIndex){
4336         for(var i = 0, len = this.config.length; i < len; i++){
4337             if(this.config[i].dataIndex == dataIndex){
4338                 return i;
4339             }
4340         }
4341         return -1;
4342     },
4343     
4344     
4345     moveColumn : function(oldIndex, newIndex){
4346         var c = this.config[oldIndex];
4347         this.config.splice(oldIndex, 1);
4348         this.config.splice(newIndex, 0, c);
4349         this.dataMap = null;
4350         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4351     },
4352
4353     isLocked : function(colIndex){
4354         return this.config[colIndex].locked === true;
4355     },
4356
4357     setLocked : function(colIndex, value, suppressEvent){
4358         if(this.isLocked(colIndex) == value){
4359             return;
4360         }
4361         this.config[colIndex].locked = value;
4362         if(!suppressEvent){
4363             this.fireEvent("columnlockchange", this, colIndex, value);
4364         }
4365     },
4366
4367     getTotalLockedWidth : function(){
4368         var totalWidth = 0;
4369         for(var i = 0; i < this.config.length; i++){
4370             if(this.isLocked(i) && !this.isHidden(i)){
4371                 this.totalWidth += this.getColumnWidth(i);
4372             }
4373         }
4374         return totalWidth;
4375     },
4376
4377     getLockedCount : function(){
4378         for(var i = 0, len = this.config.length; i < len; i++){
4379             if(!this.isLocked(i)){
4380                 return i;
4381             }
4382         }
4383     },
4384
4385     /**
4386      * Returns the number of columns.
4387      * @return {Number}
4388      */
4389     getColumnCount : function(visibleOnly){
4390         if(visibleOnly === true){
4391             var c = 0;
4392             for(var i = 0, len = this.config.length; i < len; i++){
4393                 if(!this.isHidden(i)){
4394                     c++;
4395                 }
4396             }
4397             return c;
4398         }
4399         return this.config.length;
4400     },
4401
4402     /**
4403      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4404      * @param {Function} fn
4405      * @param {Object} scope (optional)
4406      * @return {Array} result
4407      */
4408     getColumnsBy : function(fn, scope){
4409         var r = [];
4410         for(var i = 0, len = this.config.length; i < len; i++){
4411             var c = this.config[i];
4412             if(fn.call(scope||this, c, i) === true){
4413                 r[r.length] = c;
4414             }
4415         }
4416         return r;
4417     },
4418
4419     /**
4420      * Returns true if the specified column is sortable.
4421      * @param {Number} col The column index
4422      * @return {Boolean}
4423      */
4424     isSortable : function(col){
4425         if(typeof this.config[col].sortable == "undefined"){
4426             return this.defaultSortable;
4427         }
4428         return this.config[col].sortable;
4429     },
4430
4431     /**
4432      * Returns the rendering (formatting) function defined for the column.
4433      * @param {Number} col The column index.
4434      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4435      */
4436     getRenderer : function(col){
4437         if(!this.config[col].renderer){
4438             return Roo.grid.ColumnModel.defaultRenderer;
4439         }
4440         return this.config[col].renderer;
4441     },
4442
4443     /**
4444      * Sets the rendering (formatting) function for a column.
4445      * @param {Number} col The column index
4446      * @param {Function} fn The function to use to process the cell's raw data
4447      * to return HTML markup for the grid view. The render function is called with
4448      * the following parameters:<ul>
4449      * <li>Data value.</li>
4450      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4451      * <li>css A CSS style string to apply to the table cell.</li>
4452      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4453      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4454      * <li>Row index</li>
4455      * <li>Column index</li>
4456      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4457      */
4458     setRenderer : function(col, fn){
4459         this.config[col].renderer = fn;
4460     },
4461
4462     /**
4463      * Returns the width for the specified column.
4464      * @param {Number} col The column index
4465      * @return {Number}
4466      */
4467     getColumnWidth : function(col){
4468         return this.config[col].width * 1 || this.defaultWidth;
4469     },
4470
4471     /**
4472      * Sets the width for a column.
4473      * @param {Number} col The column index
4474      * @param {Number} width The new width
4475      */
4476     setColumnWidth : function(col, width, suppressEvent){
4477         this.config[col].width = width;
4478         this.totalWidth = null;
4479         if(!suppressEvent){
4480              this.fireEvent("widthchange", this, col, width);
4481         }
4482     },
4483
4484     /**
4485      * Returns the total width of all columns.
4486      * @param {Boolean} includeHidden True to include hidden column widths
4487      * @return {Number}
4488      */
4489     getTotalWidth : function(includeHidden){
4490         if(!this.totalWidth){
4491             this.totalWidth = 0;
4492             for(var i = 0, len = this.config.length; i < len; i++){
4493                 if(includeHidden || !this.isHidden(i)){
4494                     this.totalWidth += this.getColumnWidth(i);
4495                 }
4496             }
4497         }
4498         return this.totalWidth;
4499     },
4500
4501     /**
4502      * Returns the header for the specified column.
4503      * @param {Number} col The column index
4504      * @return {String}
4505      */
4506     getColumnHeader : function(col){
4507         return this.config[col].header;
4508     },
4509
4510     /**
4511      * Sets the header for a column.
4512      * @param {Number} col The column index
4513      * @param {String} header The new header
4514      */
4515     setColumnHeader : function(col, header){
4516         this.config[col].header = header;
4517         this.fireEvent("headerchange", this, col, header);
4518     },
4519
4520     /**
4521      * Returns the tooltip for the specified column.
4522      * @param {Number} col The column index
4523      * @return {String}
4524      */
4525     getColumnTooltip : function(col){
4526             return this.config[col].tooltip;
4527     },
4528     /**
4529      * Sets the tooltip for a column.
4530      * @param {Number} col The column index
4531      * @param {String} tooltip The new tooltip
4532      */
4533     setColumnTooltip : function(col, tooltip){
4534             this.config[col].tooltip = tooltip;
4535     },
4536
4537     /**
4538      * Returns the dataIndex for the specified column.
4539      * @param {Number} col The column index
4540      * @return {Number}
4541      */
4542     getDataIndex : function(col){
4543         return this.config[col].dataIndex;
4544     },
4545
4546     /**
4547      * Sets the dataIndex for a column.
4548      * @param {Number} col The column index
4549      * @param {Number} dataIndex The new dataIndex
4550      */
4551     setDataIndex : function(col, dataIndex){
4552         this.config[col].dataIndex = dataIndex;
4553     },
4554
4555     
4556     
4557     /**
4558      * Returns true if the cell is editable.
4559      * @param {Number} colIndex The column index
4560      * @param {Number} rowIndex The row index
4561      * @return {Boolean}
4562      */
4563     isCellEditable : function(colIndex, rowIndex){
4564         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4565     },
4566
4567     /**
4568      * Returns the editor defined for the cell/column.
4569      * return false or null to disable editing.
4570      * @param {Number} colIndex The column index
4571      * @param {Number} rowIndex The row index
4572      * @return {Object}
4573      */
4574     getCellEditor : function(colIndex, rowIndex){
4575         return this.config[colIndex].editor;
4576     },
4577
4578     /**
4579      * Sets if a column is editable.
4580      * @param {Number} col The column index
4581      * @param {Boolean} editable True if the column is editable
4582      */
4583     setEditable : function(col, editable){
4584         this.config[col].editable = editable;
4585     },
4586
4587
4588     /**
4589      * Returns true if the column is hidden.
4590      * @param {Number} colIndex The column index
4591      * @return {Boolean}
4592      */
4593     isHidden : function(colIndex){
4594         return this.config[colIndex].hidden;
4595     },
4596
4597
4598     /**
4599      * Returns true if the column width cannot be changed
4600      */
4601     isFixed : function(colIndex){
4602         return this.config[colIndex].fixed;
4603     },
4604
4605     /**
4606      * Returns true if the column can be resized
4607      * @return {Boolean}
4608      */
4609     isResizable : function(colIndex){
4610         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4611     },
4612     /**
4613      * Sets if a column is hidden.
4614      * @param {Number} colIndex The column index
4615      * @param {Boolean} hidden True if the column is hidden
4616      */
4617     setHidden : function(colIndex, hidden){
4618         this.config[colIndex].hidden = hidden;
4619         this.totalWidth = null;
4620         this.fireEvent("hiddenchange", this, colIndex, hidden);
4621     },
4622
4623     /**
4624      * Sets the editor for a column.
4625      * @param {Number} col The column index
4626      * @param {Object} editor The editor object
4627      */
4628     setEditor : function(col, editor){
4629         this.config[col].editor = editor;
4630     }
4631 });
4632
4633 Roo.grid.ColumnModel.defaultRenderer = function(value){
4634         if(typeof value == "string" && value.length < 1){
4635             return "&#160;";
4636         }
4637         return value;
4638 };
4639
4640 // Alias for backwards compatibility
4641 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4642 /*
4643  * Based on:
4644  * Ext JS Library 1.1.1
4645  * Copyright(c) 2006-2007, Ext JS, LLC.
4646  *
4647  * Originally Released Under LGPL - original licence link has changed is not relivant.
4648  *
4649  * Fork - LGPL
4650  * <script type="text/javascript">
4651  */
4652  
4653 /**
4654  * @class Roo.LoadMask
4655  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4656  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4657  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4658  * element's UpdateManager load indicator and will be destroyed after the initial load.
4659  * @constructor
4660  * Create a new LoadMask
4661  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4662  * @param {Object} config The config object
4663  */
4664 Roo.LoadMask = function(el, config){
4665     this.el = Roo.get(el);
4666     Roo.apply(this, config);
4667     if(this.store){
4668         this.store.on('beforeload', this.onBeforeLoad, this);
4669         this.store.on('load', this.onLoad, this);
4670         this.store.on('loadexception', this.onLoadException, this);
4671         this.removeMask = false;
4672     }else{
4673         var um = this.el.getUpdateManager();
4674         um.showLoadIndicator = false; // disable the default indicator
4675         um.on('beforeupdate', this.onBeforeLoad, this);
4676         um.on('update', this.onLoad, this);
4677         um.on('failure', this.onLoad, this);
4678         this.removeMask = true;
4679     }
4680 };
4681
4682 Roo.LoadMask.prototype = {
4683     /**
4684      * @cfg {Boolean} removeMask
4685      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4686      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4687      */
4688     /**
4689      * @cfg {String} msg
4690      * The text to display in a centered loading message box (defaults to 'Loading...')
4691      */
4692     msg : 'Loading...',
4693     /**
4694      * @cfg {String} msgCls
4695      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4696      */
4697     msgCls : 'x-mask-loading',
4698
4699     /**
4700      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4701      * @type Boolean
4702      */
4703     disabled: false,
4704
4705     /**
4706      * Disables the mask to prevent it from being displayed
4707      */
4708     disable : function(){
4709        this.disabled = true;
4710     },
4711
4712     /**
4713      * Enables the mask so that it can be displayed
4714      */
4715     enable : function(){
4716         this.disabled = false;
4717     },
4718     
4719     onLoadException : function()
4720     {
4721         Roo.log(arguments);
4722         
4723         if (typeof(arguments[3]) != 'undefined') {
4724             Roo.MessageBox.alert("Error loading",arguments[3]);
4725         } 
4726         /*
4727         try {
4728             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4729                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4730             }   
4731         } catch(e) {
4732             
4733         }
4734         */
4735     
4736         
4737         
4738         this.el.unmask(this.removeMask);
4739     },
4740     // private
4741     onLoad : function()
4742     {
4743         this.el.unmask(this.removeMask);
4744     },
4745
4746     // private
4747     onBeforeLoad : function(){
4748         if(!this.disabled){
4749             this.el.mask(this.msg, this.msgCls);
4750         }
4751     },
4752
4753     // private
4754     destroy : function(){
4755         if(this.store){
4756             this.store.un('beforeload', this.onBeforeLoad, this);
4757             this.store.un('load', this.onLoad, this);
4758             this.store.un('loadexception', this.onLoadException, this);
4759         }else{
4760             var um = this.el.getUpdateManager();
4761             um.un('beforeupdate', this.onBeforeLoad, this);
4762             um.un('update', this.onLoad, this);
4763             um.un('failure', this.onLoad, this);
4764         }
4765     }
4766 };/*
4767  * - LGPL
4768  *
4769  * table
4770  * 
4771  */
4772
4773 /**
4774  * @class Roo.bootstrap.Table
4775  * @extends Roo.bootstrap.Component
4776  * Bootstrap Table class
4777  * @cfg {String} cls table class
4778  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4779  * @cfg {String} bgcolor Specifies the background color for a table
4780  * @cfg {Number} border Specifies whether the table cells should have borders or not
4781  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4782  * @cfg {Number} cellspacing Specifies the space between cells
4783  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4784  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4785  * @cfg {String} sortable Specifies that the table should be sortable
4786  * @cfg {String} summary Specifies a summary of the content of a table
4787  * @cfg {Number} width Specifies the width of a table
4788  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4789  * 
4790  * @cfg {boolean} striped Should the rows be alternative striped
4791  * @cfg {boolean} bordered Add borders to the table
4792  * @cfg {boolean} hover Add hover highlighting
4793  * @cfg {boolean} condensed Format condensed
4794  * @cfg {boolean} responsive Format condensed
4795  * @cfg {Boolean} loadMask (true|false) default false
4796  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4797  * @cfg {Boolean} thead (true|false) generate thead, default true
4798  * @cfg {Boolean} RowSelection (true|false) default false
4799  * @cfg {Boolean} CellSelection (true|false) default false
4800  *
4801  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4802  
4803  * 
4804  * @constructor
4805  * Create a new Table
4806  * @param {Object} config The config object
4807  */
4808
4809 Roo.bootstrap.Table = function(config){
4810     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4811     
4812     if (this.sm) {
4813         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4814         this.sm = this.selModel;
4815         this.sm.xmodule = this.xmodule || false;
4816     }
4817     if (this.cm && typeof(this.cm.config) == 'undefined') {
4818         this.colModel = new Roo.grid.ColumnModel(this.cm);
4819         this.cm = this.colModel;
4820         this.cm.xmodule = this.xmodule || false;
4821     }
4822     if (this.store) {
4823         this.store= Roo.factory(this.store, Roo.data);
4824         this.ds = this.store;
4825         this.ds.xmodule = this.xmodule || false;
4826          
4827     }
4828     if (this.footer && this.store) {
4829         this.footer.dataSource = this.ds;
4830         this.footer = Roo.factory(this.footer);
4831     }
4832     
4833     /** @private */
4834     this.addEvents({
4835         /**
4836          * @event cellclick
4837          * Fires when a cell is clicked
4838          * @param {Roo.bootstrap.Table} this
4839          * @param {Roo.Element} el
4840          * @param {Number} rowIndex
4841          * @param {Number} columnIndex
4842          * @param {Roo.EventObject} e
4843          */
4844         "cellclick" : true,
4845         /**
4846          * @event celldblclick
4847          * Fires when a cell is double clicked
4848          * @param {Roo.bootstrap.Table} this
4849          * @param {Roo.Element} el
4850          * @param {Number} rowIndex
4851          * @param {Number} columnIndex
4852          * @param {Roo.EventObject} e
4853          */
4854         "celldblclick" : true,
4855         /**
4856          * @event rowclick
4857          * Fires when a row is clicked
4858          * @param {Roo.bootstrap.Table} this
4859          * @param {Roo.Element} el
4860          * @param {Number} rowIndex
4861          * @param {Roo.EventObject} e
4862          */
4863         "rowclick" : true,
4864         /**
4865          * @event rowdblclick
4866          * Fires when a row is double clicked
4867          * @param {Roo.bootstrap.Table} this
4868          * @param {Roo.Element} el
4869          * @param {Number} rowIndex
4870          * @param {Roo.EventObject} e
4871          */
4872         "rowdblclick" : true,
4873         /**
4874          * @event mouseover
4875          * Fires when a mouseover occur
4876          * @param {Roo.bootstrap.Table} this
4877          * @param {Roo.Element} el
4878          * @param {Number} rowIndex
4879          * @param {Number} columnIndex
4880          * @param {Roo.EventObject} e
4881          */
4882         "mouseover" : true,
4883         /**
4884          * @event mouseout
4885          * Fires when a mouseout occur
4886          * @param {Roo.bootstrap.Table} this
4887          * @param {Roo.Element} el
4888          * @param {Number} rowIndex
4889          * @param {Number} columnIndex
4890          * @param {Roo.EventObject} e
4891          */
4892         "mouseout" : true,
4893         /**
4894          * @event rowclass
4895          * Fires when a row is rendered, so you can change add a style to it.
4896          * @param {Roo.bootstrap.Table} this
4897          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4898          */
4899         'rowclass' : true
4900         
4901     });
4902 };
4903
4904 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4905     
4906     cls: false,
4907     align: false,
4908     bgcolor: false,
4909     border: false,
4910     cellpadding: false,
4911     cellspacing: false,
4912     frame: false,
4913     rules: false,
4914     sortable: false,
4915     summary: false,
4916     width: false,
4917     striped : false,
4918     bordered: false,
4919     hover:  false,
4920     condensed : false,
4921     responsive : false,
4922     sm : false,
4923     cm : false,
4924     store : false,
4925     loadMask : false,
4926     tfoot : true,
4927     thead : true,
4928     RowSelection : false,
4929     CellSelection : false,
4930     layout : false,
4931     
4932     // Roo.Element - the tbody
4933     mainBody: false, 
4934     
4935     getAutoCreate : function(){
4936         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4937         
4938         cfg = {
4939             tag: 'table',
4940             cls : 'table',
4941             cn : []
4942         }
4943             
4944         if (this.striped) {
4945             cfg.cls += ' table-striped';
4946         }
4947         
4948         if (this.hover) {
4949             cfg.cls += ' table-hover';
4950         }
4951         if (this.bordered) {
4952             cfg.cls += ' table-bordered';
4953         }
4954         if (this.condensed) {
4955             cfg.cls += ' table-condensed';
4956         }
4957         if (this.responsive) {
4958             cfg.cls += ' table-responsive';
4959         }
4960         
4961         if (this.cls) {
4962             cfg.cls+=  ' ' +this.cls;
4963         }
4964         
4965         // this lot should be simplifed...
4966         
4967         if (this.align) {
4968             cfg.align=this.align;
4969         }
4970         if (this.bgcolor) {
4971             cfg.bgcolor=this.bgcolor;
4972         }
4973         if (this.border) {
4974             cfg.border=this.border;
4975         }
4976         if (this.cellpadding) {
4977             cfg.cellpadding=this.cellpadding;
4978         }
4979         if (this.cellspacing) {
4980             cfg.cellspacing=this.cellspacing;
4981         }
4982         if (this.frame) {
4983             cfg.frame=this.frame;
4984         }
4985         if (this.rules) {
4986             cfg.rules=this.rules;
4987         }
4988         if (this.sortable) {
4989             cfg.sortable=this.sortable;
4990         }
4991         if (this.summary) {
4992             cfg.summary=this.summary;
4993         }
4994         if (this.width) {
4995             cfg.width=this.width;
4996         }
4997         if (this.layout) {
4998             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4999         }
5000         
5001         if(this.store || this.cm){
5002             if(this.thead){
5003                 cfg.cn.push(this.renderHeader());
5004             }
5005             
5006             cfg.cn.push(this.renderBody());
5007             
5008             if(this.tfoot){
5009                 cfg.cn.push(this.renderFooter());
5010             }
5011             
5012             cfg.cls+=  ' TableGrid';
5013         }
5014         
5015         return { cn : [ cfg ] };
5016     },
5017     
5018     initEvents : function()
5019     {   
5020         if(!this.store || !this.cm){
5021             return;
5022         }
5023         
5024         //Roo.log('initEvents with ds!!!!');
5025         
5026         this.mainBody = this.el.select('tbody', true).first();
5027         
5028         
5029         var _this = this;
5030         
5031         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5032             e.on('click', _this.sort, _this);
5033         });
5034         
5035         this.el.on("click", this.onClick, this);
5036         this.el.on("dblclick", this.onDblClick, this);
5037         
5038         this.parent().el.setStyle('position', 'relative');
5039         if (this.footer) {
5040             this.footer.parentId = this.id;
5041             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5042         }
5043         
5044         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5045         
5046         this.store.on('load', this.onLoad, this);
5047         this.store.on('beforeload', this.onBeforeLoad, this);
5048         this.store.on('update', this.onUpdate, this);
5049         
5050     },
5051     
5052     onMouseover : function(e, el)
5053     {
5054         var cell = Roo.get(el);
5055         
5056         if(!cell){
5057             return;
5058         }
5059         
5060         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5061             cell = cell.findParent('td', false, true);
5062         }
5063         
5064         var row = cell.findParent('tr', false, true);
5065         var cellIndex = cell.dom.cellIndex;
5066         var rowIndex = row.dom.rowIndex - 1; // start from 0
5067         
5068         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5069         
5070     },
5071     
5072     onMouseout : function(e, el)
5073     {
5074         var cell = Roo.get(el);
5075         
5076         if(!cell){
5077             return;
5078         }
5079         
5080         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5081             cell = cell.findParent('td', false, true);
5082         }
5083         
5084         var row = cell.findParent('tr', false, true);
5085         var cellIndex = cell.dom.cellIndex;
5086         var rowIndex = row.dom.rowIndex - 1; // start from 0
5087         
5088         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5089         
5090     },
5091     
5092     onClick : function(e, el)
5093     {
5094         var cell = Roo.get(el);
5095         
5096         if(!cell || (!this.CellSelection && !this.RowSelection)){
5097             return;
5098         }
5099         
5100         
5101         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5102             cell = cell.findParent('td', false, true);
5103         }
5104         
5105         var row = cell.findParent('tr', false, true);
5106         var cellIndex = cell.dom.cellIndex;
5107         var rowIndex = row.dom.rowIndex - 1;
5108         
5109         if(this.CellSelection){
5110             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5111         }
5112         
5113         if(this.RowSelection){
5114             this.fireEvent('rowclick', this, row, rowIndex, e);
5115         }
5116         
5117         
5118     },
5119     
5120     onDblClick : function(e,el)
5121     {
5122         var cell = Roo.get(el);
5123         
5124         if(!cell || (!this.CellSelection && !this.RowSelection)){
5125             return;
5126         }
5127         
5128         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5129             cell = cell.findParent('td', false, true);
5130         }
5131         
5132         var row = cell.findParent('tr', false, true);
5133         var cellIndex = cell.dom.cellIndex;
5134         var rowIndex = row.dom.rowIndex - 1;
5135         
5136         if(this.CellSelection){
5137             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5138         }
5139         
5140         if(this.RowSelection){
5141             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5142         }
5143     },
5144     
5145     sort : function(e,el)
5146     {
5147         var col = Roo.get(el)
5148         
5149         if(!col.hasClass('sortable')){
5150             return;
5151         }
5152         
5153         var sort = col.attr('sort');
5154         var dir = 'ASC';
5155         
5156         if(col.hasClass('glyphicon-arrow-up')){
5157             dir = 'DESC';
5158         }
5159         
5160         this.store.sortInfo = {field : sort, direction : dir};
5161         
5162         if (this.footer) {
5163             Roo.log("calling footer first");
5164             this.footer.onClick('first');
5165         } else {
5166         
5167             this.store.load({ params : { start : 0 } });
5168         }
5169     },
5170     
5171     renderHeader : function()
5172     {
5173         var header = {
5174             tag: 'thead',
5175             cn : []
5176         };
5177         
5178         var cm = this.cm;
5179         
5180         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5181             
5182             var config = cm.config[i];
5183                     
5184             var c = {
5185                 tag: 'th',
5186                 style : '',
5187                 html: cm.getColumnHeader(i)
5188             };
5189             
5190             if(typeof(config.hidden) != 'undefined' && config.hidden){
5191                 c.style += ' display:none;';
5192             }
5193             
5194             if(typeof(config.dataIndex) != 'undefined'){
5195                 c.sort = config.dataIndex;
5196             }
5197             
5198             if(typeof(config.sortable) != 'undefined' && config.sortable){
5199                 c.cls = 'sortable';
5200             }
5201             
5202             if(typeof(config.align) != 'undefined' && config.align.length){
5203                 c.style += ' text-align:' + config.align + ';';
5204             }
5205             
5206             if(typeof(config.width) != 'undefined'){
5207                 c.style += ' width:' + config.width + 'px;';
5208             }
5209             
5210             header.cn.push(c)
5211         }
5212         
5213         return header;
5214     },
5215     
5216     renderBody : function()
5217     {
5218         var body = {
5219             tag: 'tbody',
5220             cn : [
5221                 {
5222                     tag: 'tr',
5223                     cn : [
5224                         {
5225                             tag : 'td',
5226                             colspan :  this.cm.getColumnCount()
5227                         }
5228                     ]
5229                 }
5230             ]
5231         };
5232         
5233         return body;
5234     },
5235     
5236     renderFooter : function()
5237     {
5238         var footer = {
5239             tag: 'tfoot',
5240             cn : [
5241                 {
5242                     tag: 'tr',
5243                     cn : [
5244                         {
5245                             tag : 'td',
5246                             colspan :  this.cm.getColumnCount()
5247                         }
5248                     ]
5249                 }
5250             ]
5251         };
5252         
5253         return footer;
5254     },
5255     
5256     
5257     
5258     onLoad : function()
5259     {
5260         Roo.log('ds onload');
5261         this.clear();
5262         
5263         var _this = this;
5264         var cm = this.cm;
5265         var ds = this.store;
5266         
5267         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5268             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5269             
5270             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5271                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5272             }
5273             
5274             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5275                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5276             }
5277         });
5278         
5279         var tbody =  this.mainBody;
5280               
5281         if(ds.getCount() > 0){
5282             ds.data.each(function(d,rowIndex){
5283                 var row =  this.renderRow(cm, ds, rowIndex);
5284                 
5285                 tbody.createChild(row);
5286                 
5287                 var _this = this;
5288                 
5289                 if(row.cellObjects.length){
5290                     Roo.each(row.cellObjects, function(r){
5291                         _this.renderCellObject(r);
5292                     })
5293                 }
5294                 
5295             }, this);
5296         }
5297         
5298         Roo.each(this.el.select('tbody td', true).elements, function(e){
5299             e.on('mouseover', _this.onMouseover, _this);
5300         });
5301         
5302         Roo.each(this.el.select('tbody td', true).elements, function(e){
5303             e.on('mouseout', _this.onMouseout, _this);
5304         });
5305
5306         //if(this.loadMask){
5307         //    this.maskEl.hide();
5308         //}
5309     },
5310     
5311     
5312     onUpdate : function(ds,record)
5313     {
5314         this.refreshRow(record);
5315     },
5316     onRemove : function(ds, record, index, isUpdate){
5317         if(isUpdate !== true){
5318             this.fireEvent("beforerowremoved", this, index, record);
5319         }
5320         var bt = this.mainBody.dom;
5321         if(bt.rows[index]){
5322             bt.removeChild(bt.rows[index]);
5323         }
5324         
5325         if(isUpdate !== true){
5326             //this.stripeRows(index);
5327             //this.syncRowHeights(index, index);
5328             //this.layout();
5329             this.fireEvent("rowremoved", this, index, record);
5330         }
5331     },
5332     
5333     
5334     refreshRow : function(record){
5335         var ds = this.store, index;
5336         if(typeof record == 'number'){
5337             index = record;
5338             record = ds.getAt(index);
5339         }else{
5340             index = ds.indexOf(record);
5341         }
5342         this.insertRow(ds, index, true);
5343         this.onRemove(ds, record, index+1, true);
5344         //this.syncRowHeights(index, index);
5345         //this.layout();
5346         this.fireEvent("rowupdated", this, index, record);
5347     },
5348     
5349     insertRow : function(dm, rowIndex, isUpdate){
5350         
5351         if(!isUpdate){
5352             this.fireEvent("beforerowsinserted", this, rowIndex);
5353         }
5354             //var s = this.getScrollState();
5355         var row = this.renderRow(this.cm, this.store, rowIndex);
5356         // insert before rowIndex..
5357         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5358         
5359         var _this = this;
5360                 
5361         if(row.cellObjects.length){
5362             Roo.each(row.cellObjects, function(r){
5363                 _this.renderCellObject(r);
5364             })
5365         }
5366             
5367         if(!isUpdate){
5368             this.fireEvent("rowsinserted", this, rowIndex);
5369             //this.syncRowHeights(firstRow, lastRow);
5370             //this.stripeRows(firstRow);
5371             //this.layout();
5372         }
5373         
5374     },
5375     
5376     
5377     getRowDom : function(rowIndex)
5378     {
5379         // not sure if I need to check this.. but let's do it anyway..
5380         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5381                 this.mainBody.dom.rows[rowIndex] : false
5382     },
5383     // returns the object tree for a tr..
5384   
5385     
5386     renderRow : function(cm, ds, rowIndex) {
5387         
5388         var d = ds.getAt(rowIndex);
5389         
5390         var row = {
5391             tag : 'tr',
5392             cn : []
5393         };
5394             
5395         var cellObjects = [];
5396         
5397         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5398             var config = cm.config[i];
5399             
5400             var renderer = cm.getRenderer(i);
5401             var value = '';
5402             var id = false;
5403             
5404             if(typeof(renderer) !== 'undefined'){
5405                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5406             }
5407             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5408             // and are rendered into the cells after the row is rendered - using the id for the element.
5409             
5410             if(typeof(value) === 'object'){
5411                 id = Roo.id();
5412                 cellObjects.push({
5413                     container : id,
5414                     cfg : value 
5415                 })
5416             }
5417             
5418             var rowcfg = {
5419                 record: d,
5420                 rowIndex : rowIndex,
5421                 colIndex : i,
5422                 rowClass : ''
5423             }
5424
5425             this.fireEvent('rowclass', this, rowcfg);
5426             
5427             var td = {
5428                 tag: 'td',
5429                 cls : rowcfg.rowClass,
5430                 style: '',
5431                 html: (typeof(value) === 'object') ? '' : value
5432             };
5433             
5434             if (id) {
5435                 td.id = id;
5436             }
5437             
5438             if(typeof(config.hidden) != 'undefined' && config.hidden){
5439                 td.style += ' display:none;';
5440             }
5441             
5442             if(typeof(config.align) != 'undefined' && config.align.length){
5443                 td.style += ' text-align:' + config.align + ';';
5444             }
5445             
5446             if(typeof(config.width) != 'undefined'){
5447                 td.style += ' width:' +  config.width + 'px;';
5448             }
5449              
5450             row.cn.push(td);
5451            
5452         }
5453         
5454         row.cellObjects = cellObjects;
5455         
5456         return row;
5457           
5458     },
5459     
5460     
5461     
5462     onBeforeLoad : function()
5463     {
5464         //Roo.log('ds onBeforeLoad');
5465         
5466         //this.clear();
5467         
5468         //if(this.loadMask){
5469         //    this.maskEl.show();
5470         //}
5471     },
5472     
5473     clear : function()
5474     {
5475         this.el.select('tbody', true).first().dom.innerHTML = '';
5476     },
5477     
5478     getSelectionModel : function(){
5479         if(!this.selModel){
5480             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5481         }
5482         return this.selModel;
5483     },
5484     /*
5485      * Render the Roo.bootstrap object from renderder
5486      */
5487     renderCellObject : function(r)
5488     {
5489         var _this = this;
5490         
5491         var t = r.cfg.render(r.container);
5492         
5493         if(r.cfg.cn){
5494             Roo.each(r.cfg.cn, function(c){
5495                 var child = {
5496                     container: t.getChildContainer(),
5497                     cfg: c
5498                 }
5499                 _this.renderCellObject(child);
5500             })
5501         }
5502     }
5503    
5504 });
5505
5506  
5507
5508  /*
5509  * - LGPL
5510  *
5511  * table cell
5512  * 
5513  */
5514
5515 /**
5516  * @class Roo.bootstrap.TableCell
5517  * @extends Roo.bootstrap.Component
5518  * Bootstrap TableCell class
5519  * @cfg {String} html cell contain text
5520  * @cfg {String} cls cell class
5521  * @cfg {String} tag cell tag (td|th) default td
5522  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5523  * @cfg {String} align Aligns the content in a cell
5524  * @cfg {String} axis Categorizes cells
5525  * @cfg {String} bgcolor Specifies the background color of a cell
5526  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5527  * @cfg {Number} colspan Specifies the number of columns a cell should span
5528  * @cfg {String} headers Specifies one or more header cells a cell is related to
5529  * @cfg {Number} height Sets the height of a cell
5530  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5531  * @cfg {Number} rowspan Sets the number of rows a cell should span
5532  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5533  * @cfg {String} valign Vertical aligns the content in a cell
5534  * @cfg {Number} width Specifies the width of a cell
5535  * 
5536  * @constructor
5537  * Create a new TableCell
5538  * @param {Object} config The config object
5539  */
5540
5541 Roo.bootstrap.TableCell = function(config){
5542     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5543 };
5544
5545 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5546     
5547     html: false,
5548     cls: false,
5549     tag: false,
5550     abbr: false,
5551     align: false,
5552     axis: false,
5553     bgcolor: false,
5554     charoff: false,
5555     colspan: false,
5556     headers: false,
5557     height: false,
5558     nowrap: false,
5559     rowspan: false,
5560     scope: false,
5561     valign: false,
5562     width: false,
5563     
5564     
5565     getAutoCreate : function(){
5566         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5567         
5568         cfg = {
5569             tag: 'td'
5570         }
5571         
5572         if(this.tag){
5573             cfg.tag = this.tag;
5574         }
5575         
5576         if (this.html) {
5577             cfg.html=this.html
5578         }
5579         if (this.cls) {
5580             cfg.cls=this.cls
5581         }
5582         if (this.abbr) {
5583             cfg.abbr=this.abbr
5584         }
5585         if (this.align) {
5586             cfg.align=this.align
5587         }
5588         if (this.axis) {
5589             cfg.axis=this.axis
5590         }
5591         if (this.bgcolor) {
5592             cfg.bgcolor=this.bgcolor
5593         }
5594         if (this.charoff) {
5595             cfg.charoff=this.charoff
5596         }
5597         if (this.colspan) {
5598             cfg.colspan=this.colspan
5599         }
5600         if (this.headers) {
5601             cfg.headers=this.headers
5602         }
5603         if (this.height) {
5604             cfg.height=this.height
5605         }
5606         if (this.nowrap) {
5607             cfg.nowrap=this.nowrap
5608         }
5609         if (this.rowspan) {
5610             cfg.rowspan=this.rowspan
5611         }
5612         if (this.scope) {
5613             cfg.scope=this.scope
5614         }
5615         if (this.valign) {
5616             cfg.valign=this.valign
5617         }
5618         if (this.width) {
5619             cfg.width=this.width
5620         }
5621         
5622         
5623         return cfg;
5624     }
5625    
5626 });
5627
5628  
5629
5630  /*
5631  * - LGPL
5632  *
5633  * table row
5634  * 
5635  */
5636
5637 /**
5638  * @class Roo.bootstrap.TableRow
5639  * @extends Roo.bootstrap.Component
5640  * Bootstrap TableRow class
5641  * @cfg {String} cls row class
5642  * @cfg {String} align Aligns the content in a table row
5643  * @cfg {String} bgcolor Specifies a background color for a table row
5644  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5645  * @cfg {String} valign Vertical aligns the content in a table row
5646  * 
5647  * @constructor
5648  * Create a new TableRow
5649  * @param {Object} config The config object
5650  */
5651
5652 Roo.bootstrap.TableRow = function(config){
5653     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5654 };
5655
5656 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5657     
5658     cls: false,
5659     align: false,
5660     bgcolor: false,
5661     charoff: false,
5662     valign: false,
5663     
5664     getAutoCreate : function(){
5665         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5666         
5667         cfg = {
5668             tag: 'tr'
5669         }
5670             
5671         if(this.cls){
5672             cfg.cls = this.cls;
5673         }
5674         if(this.align){
5675             cfg.align = this.align;
5676         }
5677         if(this.bgcolor){
5678             cfg.bgcolor = this.bgcolor;
5679         }
5680         if(this.charoff){
5681             cfg.charoff = this.charoff;
5682         }
5683         if(this.valign){
5684             cfg.valign = this.valign;
5685         }
5686         
5687         return cfg;
5688     }
5689    
5690 });
5691
5692  
5693
5694  /*
5695  * - LGPL
5696  *
5697  * table body
5698  * 
5699  */
5700
5701 /**
5702  * @class Roo.bootstrap.TableBody
5703  * @extends Roo.bootstrap.Component
5704  * Bootstrap TableBody class
5705  * @cfg {String} cls element class
5706  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5707  * @cfg {String} align Aligns the content inside the element
5708  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5709  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5710  * 
5711  * @constructor
5712  * Create a new TableBody
5713  * @param {Object} config The config object
5714  */
5715
5716 Roo.bootstrap.TableBody = function(config){
5717     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5718 };
5719
5720 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5721     
5722     cls: false,
5723     tag: false,
5724     align: false,
5725     charoff: false,
5726     valign: false,
5727     
5728     getAutoCreate : function(){
5729         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5730         
5731         cfg = {
5732             tag: 'tbody'
5733         }
5734             
5735         if (this.cls) {
5736             cfg.cls=this.cls
5737         }
5738         if(this.tag){
5739             cfg.tag = this.tag;
5740         }
5741         
5742         if(this.align){
5743             cfg.align = this.align;
5744         }
5745         if(this.charoff){
5746             cfg.charoff = this.charoff;
5747         }
5748         if(this.valign){
5749             cfg.valign = this.valign;
5750         }
5751         
5752         return cfg;
5753     }
5754     
5755     
5756 //    initEvents : function()
5757 //    {
5758 //        
5759 //        if(!this.store){
5760 //            return;
5761 //        }
5762 //        
5763 //        this.store = Roo.factory(this.store, Roo.data);
5764 //        this.store.on('load', this.onLoad, this);
5765 //        
5766 //        this.store.load();
5767 //        
5768 //    },
5769 //    
5770 //    onLoad: function () 
5771 //    {   
5772 //        this.fireEvent('load', this);
5773 //    }
5774 //    
5775 //   
5776 });
5777
5778  
5779
5780  /*
5781  * Based on:
5782  * Ext JS Library 1.1.1
5783  * Copyright(c) 2006-2007, Ext JS, LLC.
5784  *
5785  * Originally Released Under LGPL - original licence link has changed is not relivant.
5786  *
5787  * Fork - LGPL
5788  * <script type="text/javascript">
5789  */
5790
5791 // as we use this in bootstrap.
5792 Roo.namespace('Roo.form');
5793  /**
5794  * @class Roo.form.Action
5795  * Internal Class used to handle form actions
5796  * @constructor
5797  * @param {Roo.form.BasicForm} el The form element or its id
5798  * @param {Object} config Configuration options
5799  */
5800
5801  
5802  
5803 // define the action interface
5804 Roo.form.Action = function(form, options){
5805     this.form = form;
5806     this.options = options || {};
5807 };
5808 /**
5809  * Client Validation Failed
5810  * @const 
5811  */
5812 Roo.form.Action.CLIENT_INVALID = 'client';
5813 /**
5814  * Server Validation Failed
5815  * @const 
5816  */
5817 Roo.form.Action.SERVER_INVALID = 'server';
5818  /**
5819  * Connect to Server Failed
5820  * @const 
5821  */
5822 Roo.form.Action.CONNECT_FAILURE = 'connect';
5823 /**
5824  * Reading Data from Server Failed
5825  * @const 
5826  */
5827 Roo.form.Action.LOAD_FAILURE = 'load';
5828
5829 Roo.form.Action.prototype = {
5830     type : 'default',
5831     failureType : undefined,
5832     response : undefined,
5833     result : undefined,
5834
5835     // interface method
5836     run : function(options){
5837
5838     },
5839
5840     // interface method
5841     success : function(response){
5842
5843     },
5844
5845     // interface method
5846     handleResponse : function(response){
5847
5848     },
5849
5850     // default connection failure
5851     failure : function(response){
5852         
5853         this.response = response;
5854         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5855         this.form.afterAction(this, false);
5856     },
5857
5858     processResponse : function(response){
5859         this.response = response;
5860         if(!response.responseText){
5861             return true;
5862         }
5863         this.result = this.handleResponse(response);
5864         return this.result;
5865     },
5866
5867     // utility functions used internally
5868     getUrl : function(appendParams){
5869         var url = this.options.url || this.form.url || this.form.el.dom.action;
5870         if(appendParams){
5871             var p = this.getParams();
5872             if(p){
5873                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5874             }
5875         }
5876         return url;
5877     },
5878
5879     getMethod : function(){
5880         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5881     },
5882
5883     getParams : function(){
5884         var bp = this.form.baseParams;
5885         var p = this.options.params;
5886         if(p){
5887             if(typeof p == "object"){
5888                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5889             }else if(typeof p == 'string' && bp){
5890                 p += '&' + Roo.urlEncode(bp);
5891             }
5892         }else if(bp){
5893             p = Roo.urlEncode(bp);
5894         }
5895         return p;
5896     },
5897
5898     createCallback : function(){
5899         return {
5900             success: this.success,
5901             failure: this.failure,
5902             scope: this,
5903             timeout: (this.form.timeout*1000),
5904             upload: this.form.fileUpload ? this.success : undefined
5905         };
5906     }
5907 };
5908
5909 Roo.form.Action.Submit = function(form, options){
5910     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5911 };
5912
5913 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5914     type : 'submit',
5915
5916     haveProgress : false,
5917     uploadComplete : false,
5918     
5919     // uploadProgress indicator.
5920     uploadProgress : function()
5921     {
5922         if (!this.form.progressUrl) {
5923             return;
5924         }
5925         
5926         if (!this.haveProgress) {
5927             Roo.MessageBox.progress("Uploading", "Uploading");
5928         }
5929         if (this.uploadComplete) {
5930            Roo.MessageBox.hide();
5931            return;
5932         }
5933         
5934         this.haveProgress = true;
5935    
5936         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5937         
5938         var c = new Roo.data.Connection();
5939         c.request({
5940             url : this.form.progressUrl,
5941             params: {
5942                 id : uid
5943             },
5944             method: 'GET',
5945             success : function(req){
5946                //console.log(data);
5947                 var rdata = false;
5948                 var edata;
5949                 try  {
5950                    rdata = Roo.decode(req.responseText)
5951                 } catch (e) {
5952                     Roo.log("Invalid data from server..");
5953                     Roo.log(edata);
5954                     return;
5955                 }
5956                 if (!rdata || !rdata.success) {
5957                     Roo.log(rdata);
5958                     Roo.MessageBox.alert(Roo.encode(rdata));
5959                     return;
5960                 }
5961                 var data = rdata.data;
5962                 
5963                 if (this.uploadComplete) {
5964                    Roo.MessageBox.hide();
5965                    return;
5966                 }
5967                    
5968                 if (data){
5969                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5970                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5971                     );
5972                 }
5973                 this.uploadProgress.defer(2000,this);
5974             },
5975        
5976             failure: function(data) {
5977                 Roo.log('progress url failed ');
5978                 Roo.log(data);
5979             },
5980             scope : this
5981         });
5982            
5983     },
5984     
5985     
5986     run : function()
5987     {
5988         // run get Values on the form, so it syncs any secondary forms.
5989         this.form.getValues();
5990         
5991         var o = this.options;
5992         var method = this.getMethod();
5993         var isPost = method == 'POST';
5994         if(o.clientValidation === false || this.form.isValid()){
5995             
5996             if (this.form.progressUrl) {
5997                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5998                     (new Date() * 1) + '' + Math.random());
5999                     
6000             } 
6001             
6002             
6003             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6004                 form:this.form.el.dom,
6005                 url:this.getUrl(!isPost),
6006                 method: method,
6007                 params:isPost ? this.getParams() : null,
6008                 isUpload: this.form.fileUpload
6009             }));
6010             
6011             this.uploadProgress();
6012
6013         }else if (o.clientValidation !== false){ // client validation failed
6014             this.failureType = Roo.form.Action.CLIENT_INVALID;
6015             this.form.afterAction(this, false);
6016         }
6017     },
6018
6019     success : function(response)
6020     {
6021         this.uploadComplete= true;
6022         if (this.haveProgress) {
6023             Roo.MessageBox.hide();
6024         }
6025         
6026         
6027         var result = this.processResponse(response);
6028         if(result === true || result.success){
6029             this.form.afterAction(this, true);
6030             return;
6031         }
6032         if(result.errors){
6033             this.form.markInvalid(result.errors);
6034             this.failureType = Roo.form.Action.SERVER_INVALID;
6035         }
6036         this.form.afterAction(this, false);
6037     },
6038     failure : function(response)
6039     {
6040         this.uploadComplete= true;
6041         if (this.haveProgress) {
6042             Roo.MessageBox.hide();
6043         }
6044         
6045         this.response = response;
6046         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6047         this.form.afterAction(this, false);
6048     },
6049     
6050     handleResponse : function(response){
6051         if(this.form.errorReader){
6052             var rs = this.form.errorReader.read(response);
6053             var errors = [];
6054             if(rs.records){
6055                 for(var i = 0, len = rs.records.length; i < len; i++) {
6056                     var r = rs.records[i];
6057                     errors[i] = r.data;
6058                 }
6059             }
6060             if(errors.length < 1){
6061                 errors = null;
6062             }
6063             return {
6064                 success : rs.success,
6065                 errors : errors
6066             };
6067         }
6068         var ret = false;
6069         try {
6070             ret = Roo.decode(response.responseText);
6071         } catch (e) {
6072             ret = {
6073                 success: false,
6074                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6075                 errors : []
6076             };
6077         }
6078         return ret;
6079         
6080     }
6081 });
6082
6083
6084 Roo.form.Action.Load = function(form, options){
6085     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6086     this.reader = this.form.reader;
6087 };
6088
6089 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6090     type : 'load',
6091
6092     run : function(){
6093         
6094         Roo.Ajax.request(Roo.apply(
6095                 this.createCallback(), {
6096                     method:this.getMethod(),
6097                     url:this.getUrl(false),
6098                     params:this.getParams()
6099         }));
6100     },
6101
6102     success : function(response){
6103         
6104         var result = this.processResponse(response);
6105         if(result === true || !result.success || !result.data){
6106             this.failureType = Roo.form.Action.LOAD_FAILURE;
6107             this.form.afterAction(this, false);
6108             return;
6109         }
6110         this.form.clearInvalid();
6111         this.form.setValues(result.data);
6112         this.form.afterAction(this, true);
6113     },
6114
6115     handleResponse : function(response){
6116         if(this.form.reader){
6117             var rs = this.form.reader.read(response);
6118             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6119             return {
6120                 success : rs.success,
6121                 data : data
6122             };
6123         }
6124         return Roo.decode(response.responseText);
6125     }
6126 });
6127
6128 Roo.form.Action.ACTION_TYPES = {
6129     'load' : Roo.form.Action.Load,
6130     'submit' : Roo.form.Action.Submit
6131 };/*
6132  * - LGPL
6133  *
6134  * form
6135  * 
6136  */
6137
6138 /**
6139  * @class Roo.bootstrap.Form
6140  * @extends Roo.bootstrap.Component
6141  * Bootstrap Form class
6142  * @cfg {String} method  GET | POST (default POST)
6143  * @cfg {String} labelAlign top | left (default top)
6144  * @cfg {String} align left  | right - for navbars
6145  * @cfg {Boolean} loadMask load mask when submit (default true)
6146
6147  * 
6148  * @constructor
6149  * Create a new Form
6150  * @param {Object} config The config object
6151  */
6152
6153
6154 Roo.bootstrap.Form = function(config){
6155     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6156     this.addEvents({
6157         /**
6158          * @event clientvalidation
6159          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6160          * @param {Form} this
6161          * @param {Boolean} valid true if the form has passed client-side validation
6162          */
6163         clientvalidation: true,
6164         /**
6165          * @event beforeaction
6166          * Fires before any action is performed. Return false to cancel the action.
6167          * @param {Form} this
6168          * @param {Action} action The action to be performed
6169          */
6170         beforeaction: true,
6171         /**
6172          * @event actionfailed
6173          * Fires when an action fails.
6174          * @param {Form} this
6175          * @param {Action} action The action that failed
6176          */
6177         actionfailed : true,
6178         /**
6179          * @event actioncomplete
6180          * Fires when an action is completed.
6181          * @param {Form} this
6182          * @param {Action} action The action that completed
6183          */
6184         actioncomplete : true
6185     });
6186     
6187 };
6188
6189 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6190       
6191      /**
6192      * @cfg {String} method
6193      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6194      */
6195     method : 'POST',
6196     /**
6197      * @cfg {String} url
6198      * The URL to use for form actions if one isn't supplied in the action options.
6199      */
6200     /**
6201      * @cfg {Boolean} fileUpload
6202      * Set to true if this form is a file upload.
6203      */
6204      
6205     /**
6206      * @cfg {Object} baseParams
6207      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6208      */
6209       
6210     /**
6211      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6212      */
6213     timeout: 30,
6214     /**
6215      * @cfg {Sting} align (left|right) for navbar forms
6216      */
6217     align : 'left',
6218
6219     // private
6220     activeAction : null,
6221  
6222     /**
6223      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6224      * element by passing it or its id or mask the form itself by passing in true.
6225      * @type Mixed
6226      */
6227     waitMsgTarget : false,
6228     
6229     loadMask : true,
6230     
6231     getAutoCreate : function(){
6232         
6233         var cfg = {
6234             tag: 'form',
6235             method : this.method || 'POST',
6236             id : this.id || Roo.id(),
6237             cls : ''
6238         }
6239         if (this.parent().xtype.match(/^Nav/)) {
6240             cfg.cls = 'navbar-form navbar-' + this.align;
6241             
6242         }
6243         
6244         if (this.labelAlign == 'left' ) {
6245             cfg.cls += ' form-horizontal';
6246         }
6247         
6248         
6249         return cfg;
6250     },
6251     initEvents : function()
6252     {
6253         this.el.on('submit', this.onSubmit, this);
6254         // this was added as random key presses on the form where triggering form submit.
6255         this.el.on('keypress', function(e) {
6256             if (e.getCharCode() != 13) {
6257                 return true;
6258             }
6259             // we might need to allow it for textareas.. and some other items.
6260             // check e.getTarget().
6261             
6262             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6263                 return true;
6264             }
6265         
6266             Roo.log("keypress blocked");
6267             
6268             e.preventDefault();
6269             return false;
6270         });
6271         
6272     },
6273     // private
6274     onSubmit : function(e){
6275         e.stopEvent();
6276     },
6277     
6278      /**
6279      * Returns true if client-side validation on the form is successful.
6280      * @return Boolean
6281      */
6282     isValid : function(){
6283         var items = this.getItems();
6284         var valid = true;
6285         items.each(function(f){
6286            if(!f.validate()){
6287                valid = false;
6288                
6289            }
6290         });
6291         return valid;
6292     },
6293     /**
6294      * Returns true if any fields in this form have changed since their original load.
6295      * @return Boolean
6296      */
6297     isDirty : function(){
6298         var dirty = false;
6299         var items = this.getItems();
6300         items.each(function(f){
6301            if(f.isDirty()){
6302                dirty = true;
6303                return false;
6304            }
6305            return true;
6306         });
6307         return dirty;
6308     },
6309      /**
6310      * Performs a predefined action (submit or load) or custom actions you define on this form.
6311      * @param {String} actionName The name of the action type
6312      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6313      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6314      * accept other config options):
6315      * <pre>
6316 Property          Type             Description
6317 ----------------  ---------------  ----------------------------------------------------------------------------------
6318 url               String           The url for the action (defaults to the form's url)
6319 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6320 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6321 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6322                                    validate the form on the client (defaults to false)
6323      * </pre>
6324      * @return {BasicForm} this
6325      */
6326     doAction : function(action, options){
6327         if(typeof action == 'string'){
6328             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6329         }
6330         if(this.fireEvent('beforeaction', this, action) !== false){
6331             this.beforeAction(action);
6332             action.run.defer(100, action);
6333         }
6334         return this;
6335     },
6336     
6337     // private
6338     beforeAction : function(action){
6339         var o = action.options;
6340         
6341         if(this.loadMask){
6342             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6343         }
6344         // not really supported yet.. ??
6345         
6346         //if(this.waitMsgTarget === true){
6347         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6348         //}else if(this.waitMsgTarget){
6349         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6350         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6351         //}else {
6352         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6353        // }
6354          
6355     },
6356
6357     // private
6358     afterAction : function(action, success){
6359         this.activeAction = null;
6360         var o = action.options;
6361         
6362         //if(this.waitMsgTarget === true){
6363             this.el.unmask();
6364         //}else if(this.waitMsgTarget){
6365         //    this.waitMsgTarget.unmask();
6366         //}else{
6367         //    Roo.MessageBox.updateProgress(1);
6368         //    Roo.MessageBox.hide();
6369        // }
6370         // 
6371         if(success){
6372             if(o.reset){
6373                 this.reset();
6374             }
6375             Roo.callback(o.success, o.scope, [this, action]);
6376             this.fireEvent('actioncomplete', this, action);
6377             
6378         }else{
6379             
6380             // failure condition..
6381             // we have a scenario where updates need confirming.
6382             // eg. if a locking scenario exists..
6383             // we look for { errors : { needs_confirm : true }} in the response.
6384             if (
6385                 (typeof(action.result) != 'undefined')  &&
6386                 (typeof(action.result.errors) != 'undefined')  &&
6387                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6388            ){
6389                 var _t = this;
6390                 Roo.log("not supported yet");
6391                  /*
6392                 
6393                 Roo.MessageBox.confirm(
6394                     "Change requires confirmation",
6395                     action.result.errorMsg,
6396                     function(r) {
6397                         if (r != 'yes') {
6398                             return;
6399                         }
6400                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6401                     }
6402                     
6403                 );
6404                 */
6405                 
6406                 
6407                 return;
6408             }
6409             
6410             Roo.callback(o.failure, o.scope, [this, action]);
6411             // show an error message if no failed handler is set..
6412             if (!this.hasListener('actionfailed')) {
6413                 Roo.log("need to add dialog support");
6414                 /*
6415                 Roo.MessageBox.alert("Error",
6416                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6417                         action.result.errorMsg :
6418                         "Saving Failed, please check your entries or try again"
6419                 );
6420                 */
6421             }
6422             
6423             this.fireEvent('actionfailed', this, action);
6424         }
6425         
6426     },
6427     /**
6428      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6429      * @param {String} id The value to search for
6430      * @return Field
6431      */
6432     findField : function(id){
6433         var items = this.getItems();
6434         var field = items.get(id);
6435         if(!field){
6436              items.each(function(f){
6437                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6438                     field = f;
6439                     return false;
6440                 }
6441                 return true;
6442             });
6443         }
6444         return field || null;
6445     },
6446      /**
6447      * Mark fields in this form invalid in bulk.
6448      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6449      * @return {BasicForm} this
6450      */
6451     markInvalid : function(errors){
6452         if(errors instanceof Array){
6453             for(var i = 0, len = errors.length; i < len; i++){
6454                 var fieldError = errors[i];
6455                 var f = this.findField(fieldError.id);
6456                 if(f){
6457                     f.markInvalid(fieldError.msg);
6458                 }
6459             }
6460         }else{
6461             var field, id;
6462             for(id in errors){
6463                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6464                     field.markInvalid(errors[id]);
6465                 }
6466             }
6467         }
6468         //Roo.each(this.childForms || [], function (f) {
6469         //    f.markInvalid(errors);
6470         //});
6471         
6472         return this;
6473     },
6474
6475     /**
6476      * Set values for fields in this form in bulk.
6477      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6478      * @return {BasicForm} this
6479      */
6480     setValues : function(values){
6481         if(values instanceof Array){ // array of objects
6482             for(var i = 0, len = values.length; i < len; i++){
6483                 var v = values[i];
6484                 var f = this.findField(v.id);
6485                 if(f){
6486                     f.setValue(v.value);
6487                     if(this.trackResetOnLoad){
6488                         f.originalValue = f.getValue();
6489                     }
6490                 }
6491             }
6492         }else{ // object hash
6493             var field, id;
6494             for(id in values){
6495                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6496                     
6497                     if (field.setFromData && 
6498                         field.valueField && 
6499                         field.displayField &&
6500                         // combos' with local stores can 
6501                         // be queried via setValue()
6502                         // to set their value..
6503                         (field.store && !field.store.isLocal)
6504                         ) {
6505                         // it's a combo
6506                         var sd = { };
6507                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6508                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6509                         field.setFromData(sd);
6510                         
6511                     } else {
6512                         field.setValue(values[id]);
6513                     }
6514                     
6515                     
6516                     if(this.trackResetOnLoad){
6517                         field.originalValue = field.getValue();
6518                     }
6519                 }
6520             }
6521         }
6522          
6523         //Roo.each(this.childForms || [], function (f) {
6524         //    f.setValues(values);
6525         //});
6526                 
6527         return this;
6528     },
6529
6530     /**
6531      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6532      * they are returned as an array.
6533      * @param {Boolean} asString
6534      * @return {Object}
6535      */
6536     getValues : function(asString){
6537         //if (this.childForms) {
6538             // copy values from the child forms
6539         //    Roo.each(this.childForms, function (f) {
6540         //        this.setValues(f.getValues());
6541         //    }, this);
6542         //}
6543         
6544         
6545         
6546         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6547         if(asString === true){
6548             return fs;
6549         }
6550         return Roo.urlDecode(fs);
6551     },
6552     
6553     /**
6554      * Returns the fields in this form as an object with key/value pairs. 
6555      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6556      * @return {Object}
6557      */
6558     getFieldValues : function(with_hidden)
6559     {
6560         var items = this.getItems();
6561         var ret = {};
6562         items.each(function(f){
6563             if (!f.getName()) {
6564                 return;
6565             }
6566             var v = f.getValue();
6567             if (f.inputType =='radio') {
6568                 if (typeof(ret[f.getName()]) == 'undefined') {
6569                     ret[f.getName()] = ''; // empty..
6570                 }
6571                 
6572                 if (!f.el.dom.checked) {
6573                     return;
6574                     
6575                 }
6576                 v = f.el.dom.value;
6577                 
6578             }
6579             
6580             // not sure if this supported any more..
6581             if ((typeof(v) == 'object') && f.getRawValue) {
6582                 v = f.getRawValue() ; // dates..
6583             }
6584             // combo boxes where name != hiddenName...
6585             if (f.name != f.getName()) {
6586                 ret[f.name] = f.getRawValue();
6587             }
6588             ret[f.getName()] = v;
6589         });
6590         
6591         return ret;
6592     },
6593
6594     /**
6595      * Clears all invalid messages in this form.
6596      * @return {BasicForm} this
6597      */
6598     clearInvalid : function(){
6599         var items = this.getItems();
6600         
6601         items.each(function(f){
6602            f.clearInvalid();
6603         });
6604         
6605         
6606         
6607         return this;
6608     },
6609
6610     /**
6611      * Resets this form.
6612      * @return {BasicForm} this
6613      */
6614     reset : function(){
6615         var items = this.getItems();
6616         items.each(function(f){
6617             f.reset();
6618         });
6619         
6620         Roo.each(this.childForms || [], function (f) {
6621             f.reset();
6622         });
6623        
6624         
6625         return this;
6626     },
6627     getItems : function()
6628     {
6629         var r=new Roo.util.MixedCollection(false, function(o){
6630             return o.id || (o.id = Roo.id());
6631         });
6632         var iter = function(el) {
6633             if (el.inputEl) {
6634                 r.add(el);
6635             }
6636             if (!el.items) {
6637                 return;
6638             }
6639             Roo.each(el.items,function(e) {
6640                 iter(e);
6641             });
6642             
6643             
6644         };
6645         iter(this);
6646         return r;
6647         
6648         
6649         
6650         
6651     }
6652     
6653 });
6654
6655  
6656 /*
6657  * Based on:
6658  * Ext JS Library 1.1.1
6659  * Copyright(c) 2006-2007, Ext JS, LLC.
6660  *
6661  * Originally Released Under LGPL - original licence link has changed is not relivant.
6662  *
6663  * Fork - LGPL
6664  * <script type="text/javascript">
6665  */
6666 /**
6667  * @class Roo.form.VTypes
6668  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6669  * @singleton
6670  */
6671 Roo.form.VTypes = function(){
6672     // closure these in so they are only created once.
6673     var alpha = /^[a-zA-Z_]+$/;
6674     var alphanum = /^[a-zA-Z0-9_]+$/;
6675     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6676     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6677
6678     // All these messages and functions are configurable
6679     return {
6680         /**
6681          * The function used to validate email addresses
6682          * @param {String} value The email address
6683          */
6684         'email' : function(v){
6685             return email.test(v);
6686         },
6687         /**
6688          * The error text to display when the email validation function returns false
6689          * @type String
6690          */
6691         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6692         /**
6693          * The keystroke filter mask to be applied on email input
6694          * @type RegExp
6695          */
6696         'emailMask' : /[a-z0-9_\.\-@]/i,
6697
6698         /**
6699          * The function used to validate URLs
6700          * @param {String} value The URL
6701          */
6702         'url' : function(v){
6703             return url.test(v);
6704         },
6705         /**
6706          * The error text to display when the url validation function returns false
6707          * @type String
6708          */
6709         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6710         
6711         /**
6712          * The function used to validate alpha values
6713          * @param {String} value The value
6714          */
6715         'alpha' : function(v){
6716             return alpha.test(v);
6717         },
6718         /**
6719          * The error text to display when the alpha validation function returns false
6720          * @type String
6721          */
6722         'alphaText' : 'This field should only contain letters and _',
6723         /**
6724          * The keystroke filter mask to be applied on alpha input
6725          * @type RegExp
6726          */
6727         'alphaMask' : /[a-z_]/i,
6728
6729         /**
6730          * The function used to validate alphanumeric values
6731          * @param {String} value The value
6732          */
6733         'alphanum' : function(v){
6734             return alphanum.test(v);
6735         },
6736         /**
6737          * The error text to display when the alphanumeric validation function returns false
6738          * @type String
6739          */
6740         'alphanumText' : 'This field should only contain letters, numbers and _',
6741         /**
6742          * The keystroke filter mask to be applied on alphanumeric input
6743          * @type RegExp
6744          */
6745         'alphanumMask' : /[a-z0-9_]/i
6746     };
6747 }();/*
6748  * - LGPL
6749  *
6750  * Input
6751  * 
6752  */
6753
6754 /**
6755  * @class Roo.bootstrap.Input
6756  * @extends Roo.bootstrap.Component
6757  * Bootstrap Input class
6758  * @cfg {Boolean} disabled is it disabled
6759  * @cfg {String} fieldLabel - the label associated
6760  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6761  * @cfg {String} name name of the input
6762  * @cfg {string} fieldLabel - the label associated
6763  * @cfg {string}  inputType - input / file submit ...
6764  * @cfg {string} placeholder - placeholder to put in text.
6765  * @cfg {string}  before - input group add on before
6766  * @cfg {string} after - input group add on after
6767  * @cfg {string} size - (lg|sm) or leave empty..
6768  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6769  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6770  * @cfg {Number} md colspan out of 12 for computer-sized screens
6771  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6772  * @cfg {string} value default value of the input
6773  * @cfg {Number} labelWidth set the width of label (0-12)
6774  * @cfg {String} labelAlign (top|left)
6775  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6776  * @cfg {String} align (left|center|right) Default left
6777  * 
6778  * 
6779  * @constructor
6780  * Create a new Input
6781  * @param {Object} config The config object
6782  */
6783
6784 Roo.bootstrap.Input = function(config){
6785     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6786    
6787         this.addEvents({
6788             /**
6789              * @event focus
6790              * Fires when this field receives input focus.
6791              * @param {Roo.form.Field} this
6792              */
6793             focus : true,
6794             /**
6795              * @event blur
6796              * Fires when this field loses input focus.
6797              * @param {Roo.form.Field} this
6798              */
6799             blur : true,
6800             /**
6801              * @event specialkey
6802              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6803              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6804              * @param {Roo.form.Field} this
6805              * @param {Roo.EventObject} e The event object
6806              */
6807             specialkey : true,
6808             /**
6809              * @event change
6810              * Fires just before the field blurs if the field value has changed.
6811              * @param {Roo.form.Field} this
6812              * @param {Mixed} newValue The new value
6813              * @param {Mixed} oldValue The original value
6814              */
6815             change : true,
6816             /**
6817              * @event invalid
6818              * Fires after the field has been marked as invalid.
6819              * @param {Roo.form.Field} this
6820              * @param {String} msg The validation message
6821              */
6822             invalid : true,
6823             /**
6824              * @event valid
6825              * Fires after the field has been validated with no errors.
6826              * @param {Roo.form.Field} this
6827              */
6828             valid : true,
6829              /**
6830              * @event keyup
6831              * Fires after the key up
6832              * @param {Roo.form.Field} this
6833              * @param {Roo.EventObject}  e The event Object
6834              */
6835             keyup : true
6836         });
6837 };
6838
6839 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6840      /**
6841      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6842       automatic validation (defaults to "keyup").
6843      */
6844     validationEvent : "keyup",
6845      /**
6846      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6847      */
6848     validateOnBlur : true,
6849     /**
6850      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6851      */
6852     validationDelay : 250,
6853      /**
6854      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6855      */
6856     focusClass : "x-form-focus",  // not needed???
6857     
6858        
6859     /**
6860      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6861      */
6862     invalidClass : "has-error",
6863     
6864     /**
6865      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6866      */
6867     selectOnFocus : false,
6868     
6869      /**
6870      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6871      */
6872     maskRe : null,
6873        /**
6874      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6875      */
6876     vtype : null,
6877     
6878       /**
6879      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6880      */
6881     disableKeyFilter : false,
6882     
6883        /**
6884      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6885      */
6886     disabled : false,
6887      /**
6888      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6889      */
6890     allowBlank : true,
6891     /**
6892      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6893      */
6894     blankText : "This field is required",
6895     
6896      /**
6897      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6898      */
6899     minLength : 0,
6900     /**
6901      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6902      */
6903     maxLength : Number.MAX_VALUE,
6904     /**
6905      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6906      */
6907     minLengthText : "The minimum length for this field is {0}",
6908     /**
6909      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6910      */
6911     maxLengthText : "The maximum length for this field is {0}",
6912   
6913     
6914     /**
6915      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6916      * If available, this function will be called only after the basic validators all return true, and will be passed the
6917      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6918      */
6919     validator : null,
6920     /**
6921      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6922      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6923      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6924      */
6925     regex : null,
6926     /**
6927      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6928      */
6929     regexText : "",
6930     
6931     
6932     
6933     fieldLabel : '',
6934     inputType : 'text',
6935     
6936     name : false,
6937     placeholder: false,
6938     before : false,
6939     after : false,
6940     size : false,
6941     // private
6942     hasFocus : false,
6943     preventMark: false,
6944     isFormField : true,
6945     value : '',
6946     labelWidth : 2,
6947     labelAlign : false,
6948     readOnly : false,
6949     align : false,
6950     formatedValue : false,
6951     
6952     parentLabelAlign : function()
6953     {
6954         var parent = this;
6955         while (parent.parent()) {
6956             parent = parent.parent();
6957             if (typeof(parent.labelAlign) !='undefined') {
6958                 return parent.labelAlign;
6959             }
6960         }
6961         return 'left';
6962         
6963     },
6964     
6965     getAutoCreate : function(){
6966         
6967         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6968         
6969         var id = Roo.id();
6970         
6971         var cfg = {};
6972         
6973         if(this.inputType != 'hidden'){
6974             cfg.cls = 'form-group' //input-group
6975         }
6976         
6977         var input =  {
6978             tag: 'input',
6979             id : id,
6980             type : this.inputType,
6981             value : this.value,
6982             cls : 'form-control',
6983             placeholder : this.placeholder || ''
6984             
6985         };
6986         
6987         if(this.align){
6988             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6989         }
6990         
6991         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6992             input.maxLength = this.maxLength;
6993         }
6994         
6995         if (this.disabled) {
6996             input.disabled=true;
6997         }
6998         
6999         if (this.readOnly) {
7000             input.readonly=true;
7001         }
7002         
7003         if (this.name) {
7004             input.name = this.name;
7005         }
7006         if (this.size) {
7007             input.cls += ' input-' + this.size;
7008         }
7009         var settings=this;
7010         ['xs','sm','md','lg'].map(function(size){
7011             if (settings[size]) {
7012                 cfg.cls += ' col-' + size + '-' + settings[size];
7013             }
7014         });
7015         
7016         var inputblock = input;
7017         
7018         if (this.before || this.after) {
7019             
7020             inputblock = {
7021                 cls : 'input-group',
7022                 cn :  [] 
7023             };
7024             if (this.before && typeof(this.before) == 'string') {
7025                 
7026                 inputblock.cn.push({
7027                     tag :'span',
7028                     cls : 'roo-input-before input-group-addon',
7029                     html : this.before
7030                 });
7031             }
7032             if (this.before && typeof(this.before) == 'object') {
7033                 this.before = Roo.factory(this.before);
7034                 Roo.log(this.before);
7035                 inputblock.cn.push({
7036                     tag :'span',
7037                     cls : 'roo-input-before input-group-' +
7038                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7039                 });
7040             }
7041             
7042             inputblock.cn.push(input);
7043             
7044             if (this.after && typeof(this.after) == 'string') {
7045                 inputblock.cn.push({
7046                     tag :'span',
7047                     cls : 'roo-input-after input-group-addon',
7048                     html : this.after
7049                 });
7050             }
7051             if (this.after && typeof(this.after) == 'object') {
7052                 this.after = Roo.factory(this.after);
7053                 Roo.log(this.after);
7054                 inputblock.cn.push({
7055                     tag :'span',
7056                     cls : 'roo-input-after input-group-' +
7057                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7058                 });
7059             }
7060         };
7061         
7062         if (align ==='left' && this.fieldLabel.length) {
7063                 Roo.log("left and has label");
7064                 cfg.cn = [
7065                     
7066                     {
7067                         tag: 'label',
7068                         'for' :  id,
7069                         cls : 'control-label col-sm-' + this.labelWidth,
7070                         html : this.fieldLabel
7071                         
7072                     },
7073                     {
7074                         cls : "col-sm-" + (12 - this.labelWidth), 
7075                         cn: [
7076                             inputblock
7077                         ]
7078                     }
7079                     
7080                 ];
7081         } else if ( this.fieldLabel.length) {
7082                 Roo.log(" label");
7083                  cfg.cn = [
7084                    
7085                     {
7086                         tag: 'label',
7087                         //cls : 'input-group-addon',
7088                         html : this.fieldLabel
7089                         
7090                     },
7091                     
7092                     inputblock
7093                     
7094                 ];
7095
7096         } else {
7097             
7098                 Roo.log(" no label && no align");
7099                 cfg.cn = [
7100                     
7101                         inputblock
7102                     
7103                 ];
7104                 
7105                 
7106         };
7107         Roo.log('input-parentType: ' + this.parentType);
7108         
7109         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7110            cfg.cls += ' navbar-form';
7111            Roo.log(cfg);
7112         }
7113         
7114         return cfg;
7115         
7116     },
7117     /**
7118      * return the real input element.
7119      */
7120     inputEl: function ()
7121     {
7122         return this.el.select('input.form-control',true).first();
7123     },
7124     setDisabled : function(v)
7125     {
7126         var i  = this.inputEl().dom;
7127         if (!v) {
7128             i.removeAttribute('disabled');
7129             return;
7130             
7131         }
7132         i.setAttribute('disabled','true');
7133     },
7134     initEvents : function()
7135     {
7136         
7137         this.inputEl().on("keydown" , this.fireKey,  this);
7138         this.inputEl().on("focus", this.onFocus,  this);
7139         this.inputEl().on("blur", this.onBlur,  this);
7140         
7141         this.inputEl().relayEvent('keyup', this);
7142
7143         // reference to original value for reset
7144         this.originalValue = this.getValue();
7145         //Roo.form.TextField.superclass.initEvents.call(this);
7146         if(this.validationEvent == 'keyup'){
7147             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7148             this.inputEl().on('keyup', this.filterValidation, this);
7149         }
7150         else if(this.validationEvent !== false){
7151             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7152         }
7153         
7154         if(this.selectOnFocus){
7155             this.on("focus", this.preFocus, this);
7156             
7157         }
7158         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7159             this.inputEl().on("keypress", this.filterKeys, this);
7160         }
7161        /* if(this.grow){
7162             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7163             this.el.on("click", this.autoSize,  this);
7164         }
7165         */
7166         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7167             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7168         }
7169         
7170         if (typeof(this.before) == 'object') {
7171             this.before.render(this.el.select('.roo-input-before',true).first());
7172         }
7173         if (typeof(this.after) == 'object') {
7174             this.after.render(this.el.select('.roo-input-after',true).first());
7175         }
7176         
7177         
7178     },
7179     filterValidation : function(e){
7180         if(!e.isNavKeyPress()){
7181             this.validationTask.delay(this.validationDelay);
7182         }
7183     },
7184      /**
7185      * Validates the field value
7186      * @return {Boolean} True if the value is valid, else false
7187      */
7188     validate : function(){
7189         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7190         if(this.disabled || this.validateValue(this.getRawValue())){
7191             this.clearInvalid();
7192             return true;
7193         }
7194         return false;
7195     },
7196     
7197     
7198     /**
7199      * Validates a value according to the field's validation rules and marks the field as invalid
7200      * if the validation fails
7201      * @param {Mixed} value The value to validate
7202      * @return {Boolean} True if the value is valid, else false
7203      */
7204     validateValue : function(value){
7205         if(value.length < 1)  { // if it's blank
7206              if(this.allowBlank){
7207                 this.clearInvalid();
7208                 return true;
7209              }else{
7210                 this.markInvalid(this.blankText);
7211                 return false;
7212              }
7213         }
7214         if(value.length < this.minLength){
7215             this.markInvalid(String.format(this.minLengthText, this.minLength));
7216             return false;
7217         }
7218         if(value.length > this.maxLength){
7219             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7220             return false;
7221         }
7222         if(this.vtype){
7223             var vt = Roo.form.VTypes;
7224             if(!vt[this.vtype](value, this)){
7225                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7226                 return false;
7227             }
7228         }
7229         if(typeof this.validator == "function"){
7230             var msg = this.validator(value);
7231             if(msg !== true){
7232                 this.markInvalid(msg);
7233                 return false;
7234             }
7235         }
7236         if(this.regex && !this.regex.test(value)){
7237             this.markInvalid(this.regexText);
7238             return false;
7239         }
7240         return true;
7241     },
7242
7243     
7244     
7245      // private
7246     fireKey : function(e){
7247         //Roo.log('field ' + e.getKey());
7248         if(e.isNavKeyPress()){
7249             this.fireEvent("specialkey", this, e);
7250         }
7251     },
7252     focus : function (selectText){
7253         if(this.rendered){
7254             this.inputEl().focus();
7255             if(selectText === true){
7256                 this.inputEl().dom.select();
7257             }
7258         }
7259         return this;
7260     } ,
7261     
7262     onFocus : function(){
7263         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7264            // this.el.addClass(this.focusClass);
7265         }
7266         if(!this.hasFocus){
7267             this.hasFocus = true;
7268             this.startValue = this.getValue();
7269             this.fireEvent("focus", this);
7270         }
7271     },
7272     
7273     beforeBlur : Roo.emptyFn,
7274
7275     
7276     // private
7277     onBlur : function(){
7278         this.beforeBlur();
7279         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7280             //this.el.removeClass(this.focusClass);
7281         }
7282         this.hasFocus = false;
7283         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7284             this.validate();
7285         }
7286         var v = this.getValue();
7287         if(String(v) !== String(this.startValue)){
7288             this.fireEvent('change', this, v, this.startValue);
7289         }
7290         this.fireEvent("blur", this);
7291     },
7292     
7293     /**
7294      * Resets the current field value to the originally loaded value and clears any validation messages
7295      */
7296     reset : function(){
7297         this.setValue(this.originalValue);
7298         this.clearInvalid();
7299     },
7300      /**
7301      * Returns the name of the field
7302      * @return {Mixed} name The name field
7303      */
7304     getName: function(){
7305         return this.name;
7306     },
7307      /**
7308      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7309      * @return {Mixed} value The field value
7310      */
7311     getValue : function(){
7312         
7313         var v = this.inputEl().getValue();
7314         
7315         return v;
7316     },
7317     /**
7318      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7319      * @return {Mixed} value The field value
7320      */
7321     getRawValue : function(){
7322         var v = this.inputEl().getValue();
7323         
7324         return v;
7325     },
7326     
7327     /**
7328      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7329      * @param {Mixed} value The value to set
7330      */
7331     setRawValue : function(v){
7332         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7333     },
7334     
7335     selectText : function(start, end){
7336         var v = this.getRawValue();
7337         if(v.length > 0){
7338             start = start === undefined ? 0 : start;
7339             end = end === undefined ? v.length : end;
7340             var d = this.inputEl().dom;
7341             if(d.setSelectionRange){
7342                 d.setSelectionRange(start, end);
7343             }else if(d.createTextRange){
7344                 var range = d.createTextRange();
7345                 range.moveStart("character", start);
7346                 range.moveEnd("character", v.length-end);
7347                 range.select();
7348             }
7349         }
7350     },
7351     
7352     /**
7353      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7354      * @param {Mixed} value The value to set
7355      */
7356     setValue : function(v){
7357         this.value = v;
7358         if(this.rendered){
7359             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7360             this.validate();
7361         }
7362     },
7363     
7364     /*
7365     processValue : function(value){
7366         if(this.stripCharsRe){
7367             var newValue = value.replace(this.stripCharsRe, '');
7368             if(newValue !== value){
7369                 this.setRawValue(newValue);
7370                 return newValue;
7371             }
7372         }
7373         return value;
7374     },
7375   */
7376     preFocus : function(){
7377         
7378         if(this.selectOnFocus){
7379             this.inputEl().dom.select();
7380         }
7381     },
7382     filterKeys : function(e){
7383         var k = e.getKey();
7384         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7385             return;
7386         }
7387         var c = e.getCharCode(), cc = String.fromCharCode(c);
7388         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7389             return;
7390         }
7391         if(!this.maskRe.test(cc)){
7392             e.stopEvent();
7393         }
7394     },
7395      /**
7396      * Clear any invalid styles/messages for this field
7397      */
7398     clearInvalid : function(){
7399         
7400         if(!this.el || this.preventMark){ // not rendered
7401             return;
7402         }
7403         this.el.removeClass(this.invalidClass);
7404         /*
7405         switch(this.msgTarget){
7406             case 'qtip':
7407                 this.el.dom.qtip = '';
7408                 break;
7409             case 'title':
7410                 this.el.dom.title = '';
7411                 break;
7412             case 'under':
7413                 if(this.errorEl){
7414                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7415                 }
7416                 break;
7417             case 'side':
7418                 if(this.errorIcon){
7419                     this.errorIcon.dom.qtip = '';
7420                     this.errorIcon.hide();
7421                     this.un('resize', this.alignErrorIcon, this);
7422                 }
7423                 break;
7424             default:
7425                 var t = Roo.getDom(this.msgTarget);
7426                 t.innerHTML = '';
7427                 t.style.display = 'none';
7428                 break;
7429         }
7430         */
7431         this.fireEvent('valid', this);
7432     },
7433      /**
7434      * Mark this field as invalid
7435      * @param {String} msg The validation message
7436      */
7437     markInvalid : function(msg){
7438         if(!this.el  || this.preventMark){ // not rendered
7439             return;
7440         }
7441         this.el.addClass(this.invalidClass);
7442         /*
7443         msg = msg || this.invalidText;
7444         switch(this.msgTarget){
7445             case 'qtip':
7446                 this.el.dom.qtip = msg;
7447                 this.el.dom.qclass = 'x-form-invalid-tip';
7448                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7449                     Roo.QuickTips.enable();
7450                 }
7451                 break;
7452             case 'title':
7453                 this.el.dom.title = msg;
7454                 break;
7455             case 'under':
7456                 if(!this.errorEl){
7457                     var elp = this.el.findParent('.x-form-element', 5, true);
7458                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7459                     this.errorEl.setWidth(elp.getWidth(true)-20);
7460                 }
7461                 this.errorEl.update(msg);
7462                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7463                 break;
7464             case 'side':
7465                 if(!this.errorIcon){
7466                     var elp = this.el.findParent('.x-form-element', 5, true);
7467                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7468                 }
7469                 this.alignErrorIcon();
7470                 this.errorIcon.dom.qtip = msg;
7471                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7472                 this.errorIcon.show();
7473                 this.on('resize', this.alignErrorIcon, this);
7474                 break;
7475             default:
7476                 var t = Roo.getDom(this.msgTarget);
7477                 t.innerHTML = msg;
7478                 t.style.display = this.msgDisplay;
7479                 break;
7480         }
7481         */
7482         this.fireEvent('invalid', this, msg);
7483     },
7484     // private
7485     SafariOnKeyDown : function(event)
7486     {
7487         // this is a workaround for a password hang bug on chrome/ webkit.
7488         
7489         var isSelectAll = false;
7490         
7491         if(this.inputEl().dom.selectionEnd > 0){
7492             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7493         }
7494         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7495             event.preventDefault();
7496             this.setValue('');
7497             return;
7498         }
7499         
7500         if(isSelectAll){ // backspace and delete key
7501             
7502             event.preventDefault();
7503             // this is very hacky as keydown always get's upper case.
7504             //
7505             var cc = String.fromCharCode(event.getCharCode());
7506             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7507             
7508         }
7509     },
7510     adjustWidth : function(tag, w){
7511         tag = tag.toLowerCase();
7512         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7513             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7514                 if(tag == 'input'){
7515                     return w + 2;
7516                 }
7517                 if(tag == 'textarea'){
7518                     return w-2;
7519                 }
7520             }else if(Roo.isOpera){
7521                 if(tag == 'input'){
7522                     return w + 2;
7523                 }
7524                 if(tag == 'textarea'){
7525                     return w-2;
7526                 }
7527             }
7528         }
7529         return w;
7530     }
7531     
7532 });
7533
7534  
7535 /*
7536  * - LGPL
7537  *
7538  * Input
7539  * 
7540  */
7541
7542 /**
7543  * @class Roo.bootstrap.TextArea
7544  * @extends Roo.bootstrap.Input
7545  * Bootstrap TextArea class
7546  * @cfg {Number} cols Specifies the visible width of a text area
7547  * @cfg {Number} rows Specifies the visible number of lines in a text area
7548  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7549  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7550  * @cfg {string} html text
7551  * 
7552  * @constructor
7553  * Create a new TextArea
7554  * @param {Object} config The config object
7555  */
7556
7557 Roo.bootstrap.TextArea = function(config){
7558     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7559    
7560 };
7561
7562 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7563      
7564     cols : false,
7565     rows : 5,
7566     readOnly : false,
7567     warp : 'soft',
7568     resize : false,
7569     value: false,
7570     html: false,
7571     
7572     getAutoCreate : function(){
7573         
7574         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7575         
7576         var id = Roo.id();
7577         
7578         var cfg = {};
7579         
7580         var input =  {
7581             tag: 'textarea',
7582             id : id,
7583             warp : this.warp,
7584             rows : this.rows,
7585             value : this.value || '',
7586             html: this.html || '',
7587             cls : 'form-control',
7588             placeholder : this.placeholder || '' 
7589             
7590         };
7591         
7592         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7593             input.maxLength = this.maxLength;
7594         }
7595         
7596         if(this.resize){
7597             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7598         }
7599         
7600         if(this.cols){
7601             input.cols = this.cols;
7602         }
7603         
7604         if (this.readOnly) {
7605             input.readonly = true;
7606         }
7607         
7608         if (this.name) {
7609             input.name = this.name;
7610         }
7611         
7612         if (this.size) {
7613             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7614         }
7615         
7616         var settings=this;
7617         ['xs','sm','md','lg'].map(function(size){
7618             if (settings[size]) {
7619                 cfg.cls += ' col-' + size + '-' + settings[size];
7620             }
7621         });
7622         
7623         var inputblock = input;
7624         
7625         if (this.before || this.after) {
7626             
7627             inputblock = {
7628                 cls : 'input-group',
7629                 cn :  [] 
7630             };
7631             if (this.before) {
7632                 inputblock.cn.push({
7633                     tag :'span',
7634                     cls : 'input-group-addon',
7635                     html : this.before
7636                 });
7637             }
7638             inputblock.cn.push(input);
7639             if (this.after) {
7640                 inputblock.cn.push({
7641                     tag :'span',
7642                     cls : 'input-group-addon',
7643                     html : this.after
7644                 });
7645             }
7646             
7647         }
7648         
7649         if (align ==='left' && this.fieldLabel.length) {
7650                 Roo.log("left and has label");
7651                 cfg.cn = [
7652                     
7653                     {
7654                         tag: 'label',
7655                         'for' :  id,
7656                         cls : 'control-label col-sm-' + this.labelWidth,
7657                         html : this.fieldLabel
7658                         
7659                     },
7660                     {
7661                         cls : "col-sm-" + (12 - this.labelWidth), 
7662                         cn: [
7663                             inputblock
7664                         ]
7665                     }
7666                     
7667                 ];
7668         } else if ( this.fieldLabel.length) {
7669                 Roo.log(" label");
7670                  cfg.cn = [
7671                    
7672                     {
7673                         tag: 'label',
7674                         //cls : 'input-group-addon',
7675                         html : this.fieldLabel
7676                         
7677                     },
7678                     
7679                     inputblock
7680                     
7681                 ];
7682
7683         } else {
7684             
7685                    Roo.log(" no label && no align");
7686                 cfg.cn = [
7687                     
7688                         inputblock
7689                     
7690                 ];
7691                 
7692                 
7693         }
7694         
7695         if (this.disabled) {
7696             input.disabled=true;
7697         }
7698         
7699         return cfg;
7700         
7701     },
7702     /**
7703      * return the real textarea element.
7704      */
7705     inputEl: function ()
7706     {
7707         return this.el.select('textarea.form-control',true).first();
7708     }
7709 });
7710
7711  
7712 /*
7713  * - LGPL
7714  *
7715  * trigger field - base class for combo..
7716  * 
7717  */
7718  
7719 /**
7720  * @class Roo.bootstrap.TriggerField
7721  * @extends Roo.bootstrap.Input
7722  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7723  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7724  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7725  * for which you can provide a custom implementation.  For example:
7726  * <pre><code>
7727 var trigger = new Roo.bootstrap.TriggerField();
7728 trigger.onTriggerClick = myTriggerFn;
7729 trigger.applyTo('my-field');
7730 </code></pre>
7731  *
7732  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7733  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7734  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7735  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7736  * @constructor
7737  * Create a new TriggerField.
7738  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7739  * to the base TextField)
7740  */
7741 Roo.bootstrap.TriggerField = function(config){
7742     this.mimicing = false;
7743     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7744 };
7745
7746 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7747     /**
7748      * @cfg {String} triggerClass A CSS class to apply to the trigger
7749      */
7750      /**
7751      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7752      */
7753     hideTrigger:false,
7754
7755     /** @cfg {Boolean} grow @hide */
7756     /** @cfg {Number} growMin @hide */
7757     /** @cfg {Number} growMax @hide */
7758
7759     /**
7760      * @hide 
7761      * @method
7762      */
7763     autoSize: Roo.emptyFn,
7764     // private
7765     monitorTab : true,
7766     // private
7767     deferHeight : true,
7768
7769     
7770     actionMode : 'wrap',
7771     
7772     
7773     
7774     getAutoCreate : function(){
7775        
7776         var align = this.labelAlign || this.parentLabelAlign();
7777         
7778         var id = Roo.id();
7779         
7780         var cfg = {
7781             cls: 'form-group' //input-group
7782         };
7783         
7784         
7785         var input =  {
7786             tag: 'input',
7787             id : id,
7788             type : this.inputType,
7789             cls : 'form-control',
7790             autocomplete: 'off',
7791             placeholder : this.placeholder || '' 
7792             
7793         };
7794         if (this.name) {
7795             input.name = this.name;
7796         }
7797         if (this.size) {
7798             input.cls += ' input-' + this.size;
7799         }
7800         
7801         if (this.disabled) {
7802             input.disabled=true;
7803         }
7804         
7805         var inputblock = input;
7806         
7807         if (this.before || this.after) {
7808             
7809             inputblock = {
7810                 cls : 'input-group',
7811                 cn :  [] 
7812             };
7813             if (this.before) {
7814                 inputblock.cn.push({
7815                     tag :'span',
7816                     cls : 'input-group-addon',
7817                     html : this.before
7818                 });
7819             }
7820             inputblock.cn.push(input);
7821             if (this.after) {
7822                 inputblock.cn.push({
7823                     tag :'span',
7824                     cls : 'input-group-addon',
7825                     html : this.after
7826                 });
7827             }
7828             
7829         };
7830         
7831         var box = {
7832             tag: 'div',
7833             cn: [
7834                 {
7835                     tag: 'input',
7836                     type : 'hidden',
7837                     cls: 'form-hidden-field'
7838                 },
7839                 inputblock
7840             ]
7841             
7842         };
7843         
7844         if(this.multiple){
7845             Roo.log('multiple');
7846             
7847             box = {
7848                 tag: 'div',
7849                 cn: [
7850                     {
7851                         tag: 'input',
7852                         type : 'hidden',
7853                         cls: 'form-hidden-field'
7854                     },
7855                     {
7856                         tag: 'ul',
7857                         cls: 'select2-choices',
7858                         cn:[
7859                             {
7860                                 tag: 'li',
7861                                 cls: 'select2-search-field',
7862                                 cn: [
7863
7864                                     inputblock
7865                                 ]
7866                             }
7867                         ]
7868                     }
7869                 ]
7870             }
7871         };
7872         
7873         var combobox = {
7874             cls: 'select2-container input-group',
7875             cn: [
7876                 box
7877 //                {
7878 //                    tag: 'ul',
7879 //                    cls: 'typeahead typeahead-long dropdown-menu',
7880 //                    style: 'display:none'
7881 //                }
7882             ]
7883         };
7884         
7885         if(!this.multiple && this.showToggleBtn){
7886             combobox.cn.push({
7887                 tag :'span',
7888                 cls : 'input-group-addon btn dropdown-toggle',
7889                 cn : [
7890                     {
7891                         tag: 'span',
7892                         cls: 'caret'
7893                     },
7894                     {
7895                         tag: 'span',
7896                         cls: 'combobox-clear',
7897                         cn  : [
7898                             {
7899                                 tag : 'i',
7900                                 cls: 'icon-remove'
7901                             }
7902                         ]
7903                     }
7904                 ]
7905
7906             })
7907         }
7908         
7909         if(this.multiple){
7910             combobox.cls += ' select2-container-multi';
7911         }
7912         
7913         if (align ==='left' && this.fieldLabel.length) {
7914             
7915                 Roo.log("left and has label");
7916                 cfg.cn = [
7917                     
7918                     {
7919                         tag: 'label',
7920                         'for' :  id,
7921                         cls : 'control-label col-sm-' + this.labelWidth,
7922                         html : this.fieldLabel
7923                         
7924                     },
7925                     {
7926                         cls : "col-sm-" + (12 - this.labelWidth), 
7927                         cn: [
7928                             combobox
7929                         ]
7930                     }
7931                     
7932                 ];
7933         } else if ( this.fieldLabel.length) {
7934                 Roo.log(" label");
7935                  cfg.cn = [
7936                    
7937                     {
7938                         tag: 'label',
7939                         //cls : 'input-group-addon',
7940                         html : this.fieldLabel
7941                         
7942                     },
7943                     
7944                     combobox
7945                     
7946                 ];
7947
7948         } else {
7949             
7950                 Roo.log(" no label && no align");
7951                 cfg = combobox
7952                      
7953                 
7954         }
7955          
7956         var settings=this;
7957         ['xs','sm','md','lg'].map(function(size){
7958             if (settings[size]) {
7959                 cfg.cls += ' col-' + size + '-' + settings[size];
7960             }
7961         });
7962         
7963         return cfg;
7964         
7965     },
7966     
7967     
7968     
7969     // private
7970     onResize : function(w, h){
7971 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7972 //        if(typeof w == 'number'){
7973 //            var x = w - this.trigger.getWidth();
7974 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7975 //            this.trigger.setStyle('left', x+'px');
7976 //        }
7977     },
7978
7979     // private
7980     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7981
7982     // private
7983     getResizeEl : function(){
7984         return this.inputEl();
7985     },
7986
7987     // private
7988     getPositionEl : function(){
7989         return this.inputEl();
7990     },
7991
7992     // private
7993     alignErrorIcon : function(){
7994         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7995     },
7996
7997     // private
7998     initEvents : function(){
7999         
8000         this.createList();
8001         
8002         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8003         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8004         if(!this.multiple && this.showToggleBtn){
8005             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8006             if(this.hideTrigger){
8007                 this.trigger.setDisplayed(false);
8008             }
8009             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8010         }
8011         
8012         if(this.multiple){
8013             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8014         }
8015         
8016         //this.trigger.addClassOnOver('x-form-trigger-over');
8017         //this.trigger.addClassOnClick('x-form-trigger-click');
8018         
8019         //if(!this.width){
8020         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8021         //}
8022     },
8023     
8024     createList : function()
8025     {
8026         this.list = Roo.get(document.body).createChild({
8027             tag: 'ul',
8028             cls: 'typeahead typeahead-long dropdown-menu',
8029             style: 'display:none'
8030         });
8031         
8032         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8033         
8034     },
8035
8036     // private
8037     initTrigger : function(){
8038        
8039     },
8040
8041     // private
8042     onDestroy : function(){
8043         if(this.trigger){
8044             this.trigger.removeAllListeners();
8045           //  this.trigger.remove();
8046         }
8047         //if(this.wrap){
8048         //    this.wrap.remove();
8049         //}
8050         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8051     },
8052
8053     // private
8054     onFocus : function(){
8055         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8056         /*
8057         if(!this.mimicing){
8058             this.wrap.addClass('x-trigger-wrap-focus');
8059             this.mimicing = true;
8060             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8061             if(this.monitorTab){
8062                 this.el.on("keydown", this.checkTab, this);
8063             }
8064         }
8065         */
8066     },
8067
8068     // private
8069     checkTab : function(e){
8070         if(e.getKey() == e.TAB){
8071             this.triggerBlur();
8072         }
8073     },
8074
8075     // private
8076     onBlur : function(){
8077         // do nothing
8078     },
8079
8080     // private
8081     mimicBlur : function(e, t){
8082         /*
8083         if(!this.wrap.contains(t) && this.validateBlur()){
8084             this.triggerBlur();
8085         }
8086         */
8087     },
8088
8089     // private
8090     triggerBlur : function(){
8091         this.mimicing = false;
8092         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8093         if(this.monitorTab){
8094             this.el.un("keydown", this.checkTab, this);
8095         }
8096         //this.wrap.removeClass('x-trigger-wrap-focus');
8097         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8098     },
8099
8100     // private
8101     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8102     validateBlur : function(e, t){
8103         return true;
8104     },
8105
8106     // private
8107     onDisable : function(){
8108         this.inputEl().dom.disabled = true;
8109         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8110         //if(this.wrap){
8111         //    this.wrap.addClass('x-item-disabled');
8112         //}
8113     },
8114
8115     // private
8116     onEnable : function(){
8117         this.inputEl().dom.disabled = false;
8118         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8119         //if(this.wrap){
8120         //    this.el.removeClass('x-item-disabled');
8121         //}
8122     },
8123
8124     // private
8125     onShow : function(){
8126         var ae = this.getActionEl();
8127         
8128         if(ae){
8129             ae.dom.style.display = '';
8130             ae.dom.style.visibility = 'visible';
8131         }
8132     },
8133
8134     // private
8135     
8136     onHide : function(){
8137         var ae = this.getActionEl();
8138         ae.dom.style.display = 'none';
8139     },
8140
8141     /**
8142      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8143      * by an implementing function.
8144      * @method
8145      * @param {EventObject} e
8146      */
8147     onTriggerClick : Roo.emptyFn
8148 });
8149  /*
8150  * Based on:
8151  * Ext JS Library 1.1.1
8152  * Copyright(c) 2006-2007, Ext JS, LLC.
8153  *
8154  * Originally Released Under LGPL - original licence link has changed is not relivant.
8155  *
8156  * Fork - LGPL
8157  * <script type="text/javascript">
8158  */
8159
8160
8161 /**
8162  * @class Roo.data.SortTypes
8163  * @singleton
8164  * Defines the default sorting (casting?) comparison functions used when sorting data.
8165  */
8166 Roo.data.SortTypes = {
8167     /**
8168      * Default sort that does nothing
8169      * @param {Mixed} s The value being converted
8170      * @return {Mixed} The comparison value
8171      */
8172     none : function(s){
8173         return s;
8174     },
8175     
8176     /**
8177      * The regular expression used to strip tags
8178      * @type {RegExp}
8179      * @property
8180      */
8181     stripTagsRE : /<\/?[^>]+>/gi,
8182     
8183     /**
8184      * Strips all HTML tags to sort on text only
8185      * @param {Mixed} s The value being converted
8186      * @return {String} The comparison value
8187      */
8188     asText : function(s){
8189         return String(s).replace(this.stripTagsRE, "");
8190     },
8191     
8192     /**
8193      * Strips all HTML tags to sort on text only - Case insensitive
8194      * @param {Mixed} s The value being converted
8195      * @return {String} The comparison value
8196      */
8197     asUCText : function(s){
8198         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8199     },
8200     
8201     /**
8202      * Case insensitive string
8203      * @param {Mixed} s The value being converted
8204      * @return {String} The comparison value
8205      */
8206     asUCString : function(s) {
8207         return String(s).toUpperCase();
8208     },
8209     
8210     /**
8211      * Date sorting
8212      * @param {Mixed} s The value being converted
8213      * @return {Number} The comparison value
8214      */
8215     asDate : function(s) {
8216         if(!s){
8217             return 0;
8218         }
8219         if(s instanceof Date){
8220             return s.getTime();
8221         }
8222         return Date.parse(String(s));
8223     },
8224     
8225     /**
8226      * Float sorting
8227      * @param {Mixed} s The value being converted
8228      * @return {Float} The comparison value
8229      */
8230     asFloat : function(s) {
8231         var val = parseFloat(String(s).replace(/,/g, ""));
8232         if(isNaN(val)) val = 0;
8233         return val;
8234     },
8235     
8236     /**
8237      * Integer sorting
8238      * @param {Mixed} s The value being converted
8239      * @return {Number} The comparison value
8240      */
8241     asInt : function(s) {
8242         var val = parseInt(String(s).replace(/,/g, ""));
8243         if(isNaN(val)) val = 0;
8244         return val;
8245     }
8246 };/*
8247  * Based on:
8248  * Ext JS Library 1.1.1
8249  * Copyright(c) 2006-2007, Ext JS, LLC.
8250  *
8251  * Originally Released Under LGPL - original licence link has changed is not relivant.
8252  *
8253  * Fork - LGPL
8254  * <script type="text/javascript">
8255  */
8256
8257 /**
8258 * @class Roo.data.Record
8259  * Instances of this class encapsulate both record <em>definition</em> information, and record
8260  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8261  * to access Records cached in an {@link Roo.data.Store} object.<br>
8262  * <p>
8263  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8264  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8265  * objects.<br>
8266  * <p>
8267  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8268  * @constructor
8269  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8270  * {@link #create}. The parameters are the same.
8271  * @param {Array} data An associative Array of data values keyed by the field name.
8272  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8273  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8274  * not specified an integer id is generated.
8275  */
8276 Roo.data.Record = function(data, id){
8277     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8278     this.data = data;
8279 };
8280
8281 /**
8282  * Generate a constructor for a specific record layout.
8283  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8284  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8285  * Each field definition object may contain the following properties: <ul>
8286  * <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,
8287  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8288  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8289  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8290  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8291  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8292  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8293  * this may be omitted.</p></li>
8294  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8295  * <ul><li>auto (Default, implies no conversion)</li>
8296  * <li>string</li>
8297  * <li>int</li>
8298  * <li>float</li>
8299  * <li>boolean</li>
8300  * <li>date</li></ul></p></li>
8301  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8302  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8303  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8304  * by the Reader into an object that will be stored in the Record. It is passed the
8305  * following parameters:<ul>
8306  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8307  * </ul></p></li>
8308  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8309  * </ul>
8310  * <br>usage:<br><pre><code>
8311 var TopicRecord = Roo.data.Record.create(
8312     {name: 'title', mapping: 'topic_title'},
8313     {name: 'author', mapping: 'username'},
8314     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8315     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8316     {name: 'lastPoster', mapping: 'user2'},
8317     {name: 'excerpt', mapping: 'post_text'}
8318 );
8319
8320 var myNewRecord = new TopicRecord({
8321     title: 'Do my job please',
8322     author: 'noobie',
8323     totalPosts: 1,
8324     lastPost: new Date(),
8325     lastPoster: 'Animal',
8326     excerpt: 'No way dude!'
8327 });
8328 myStore.add(myNewRecord);
8329 </code></pre>
8330  * @method create
8331  * @static
8332  */
8333 Roo.data.Record.create = function(o){
8334     var f = function(){
8335         f.superclass.constructor.apply(this, arguments);
8336     };
8337     Roo.extend(f, Roo.data.Record);
8338     var p = f.prototype;
8339     p.fields = new Roo.util.MixedCollection(false, function(field){
8340         return field.name;
8341     });
8342     for(var i = 0, len = o.length; i < len; i++){
8343         p.fields.add(new Roo.data.Field(o[i]));
8344     }
8345     f.getField = function(name){
8346         return p.fields.get(name);  
8347     };
8348     return f;
8349 };
8350
8351 Roo.data.Record.AUTO_ID = 1000;
8352 Roo.data.Record.EDIT = 'edit';
8353 Roo.data.Record.REJECT = 'reject';
8354 Roo.data.Record.COMMIT = 'commit';
8355
8356 Roo.data.Record.prototype = {
8357     /**
8358      * Readonly flag - true if this record has been modified.
8359      * @type Boolean
8360      */
8361     dirty : false,
8362     editing : false,
8363     error: null,
8364     modified: null,
8365
8366     // private
8367     join : function(store){
8368         this.store = store;
8369     },
8370
8371     /**
8372      * Set the named field to the specified value.
8373      * @param {String} name The name of the field to set.
8374      * @param {Object} value The value to set the field to.
8375      */
8376     set : function(name, value){
8377         if(this.data[name] == value){
8378             return;
8379         }
8380         this.dirty = true;
8381         if(!this.modified){
8382             this.modified = {};
8383         }
8384         if(typeof this.modified[name] == 'undefined'){
8385             this.modified[name] = this.data[name];
8386         }
8387         this.data[name] = value;
8388         if(!this.editing && this.store){
8389             this.store.afterEdit(this);
8390         }       
8391     },
8392
8393     /**
8394      * Get the value of the named field.
8395      * @param {String} name The name of the field to get the value of.
8396      * @return {Object} The value of the field.
8397      */
8398     get : function(name){
8399         return this.data[name]; 
8400     },
8401
8402     // private
8403     beginEdit : function(){
8404         this.editing = true;
8405         this.modified = {}; 
8406     },
8407
8408     // private
8409     cancelEdit : function(){
8410         this.editing = false;
8411         delete this.modified;
8412     },
8413
8414     // private
8415     endEdit : function(){
8416         this.editing = false;
8417         if(this.dirty && this.store){
8418             this.store.afterEdit(this);
8419         }
8420     },
8421
8422     /**
8423      * Usually called by the {@link Roo.data.Store} which owns the Record.
8424      * Rejects all changes made to the Record since either creation, or the last commit operation.
8425      * Modified fields are reverted to their original values.
8426      * <p>
8427      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8428      * of reject operations.
8429      */
8430     reject : function(){
8431         var m = this.modified;
8432         for(var n in m){
8433             if(typeof m[n] != "function"){
8434                 this.data[n] = m[n];
8435             }
8436         }
8437         this.dirty = false;
8438         delete this.modified;
8439         this.editing = false;
8440         if(this.store){
8441             this.store.afterReject(this);
8442         }
8443     },
8444
8445     /**
8446      * Usually called by the {@link Roo.data.Store} which owns the Record.
8447      * Commits all changes made to the Record since either creation, or the last commit operation.
8448      * <p>
8449      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8450      * of commit operations.
8451      */
8452     commit : function(){
8453         this.dirty = false;
8454         delete this.modified;
8455         this.editing = false;
8456         if(this.store){
8457             this.store.afterCommit(this);
8458         }
8459     },
8460
8461     // private
8462     hasError : function(){
8463         return this.error != null;
8464     },
8465
8466     // private
8467     clearError : function(){
8468         this.error = null;
8469     },
8470
8471     /**
8472      * Creates a copy of this record.
8473      * @param {String} id (optional) A new record id if you don't want to use this record's id
8474      * @return {Record}
8475      */
8476     copy : function(newId) {
8477         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8478     }
8479 };/*
8480  * Based on:
8481  * Ext JS Library 1.1.1
8482  * Copyright(c) 2006-2007, Ext JS, LLC.
8483  *
8484  * Originally Released Under LGPL - original licence link has changed is not relivant.
8485  *
8486  * Fork - LGPL
8487  * <script type="text/javascript">
8488  */
8489
8490
8491
8492 /**
8493  * @class Roo.data.Store
8494  * @extends Roo.util.Observable
8495  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8496  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8497  * <p>
8498  * 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
8499  * has no knowledge of the format of the data returned by the Proxy.<br>
8500  * <p>
8501  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8502  * instances from the data object. These records are cached and made available through accessor functions.
8503  * @constructor
8504  * Creates a new Store.
8505  * @param {Object} config A config object containing the objects needed for the Store to access data,
8506  * and read the data into Records.
8507  */
8508 Roo.data.Store = function(config){
8509     this.data = new Roo.util.MixedCollection(false);
8510     this.data.getKey = function(o){
8511         return o.id;
8512     };
8513     this.baseParams = {};
8514     // private
8515     this.paramNames = {
8516         "start" : "start",
8517         "limit" : "limit",
8518         "sort" : "sort",
8519         "dir" : "dir",
8520         "multisort" : "_multisort"
8521     };
8522
8523     if(config && config.data){
8524         this.inlineData = config.data;
8525         delete config.data;
8526     }
8527
8528     Roo.apply(this, config);
8529     
8530     if(this.reader){ // reader passed
8531         this.reader = Roo.factory(this.reader, Roo.data);
8532         this.reader.xmodule = this.xmodule || false;
8533         if(!this.recordType){
8534             this.recordType = this.reader.recordType;
8535         }
8536         if(this.reader.onMetaChange){
8537             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8538         }
8539     }
8540
8541     if(this.recordType){
8542         this.fields = this.recordType.prototype.fields;
8543     }
8544     this.modified = [];
8545
8546     this.addEvents({
8547         /**
8548          * @event datachanged
8549          * Fires when the data cache has changed, and a widget which is using this Store
8550          * as a Record cache should refresh its view.
8551          * @param {Store} this
8552          */
8553         datachanged : true,
8554         /**
8555          * @event metachange
8556          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8557          * @param {Store} this
8558          * @param {Object} meta The JSON metadata
8559          */
8560         metachange : true,
8561         /**
8562          * @event add
8563          * Fires when Records have been added to the Store
8564          * @param {Store} this
8565          * @param {Roo.data.Record[]} records The array of Records added
8566          * @param {Number} index The index at which the record(s) were added
8567          */
8568         add : true,
8569         /**
8570          * @event remove
8571          * Fires when a Record has been removed from the Store
8572          * @param {Store} this
8573          * @param {Roo.data.Record} record The Record that was removed
8574          * @param {Number} index The index at which the record was removed
8575          */
8576         remove : true,
8577         /**
8578          * @event update
8579          * Fires when a Record has been updated
8580          * @param {Store} this
8581          * @param {Roo.data.Record} record The Record that was updated
8582          * @param {String} operation The update operation being performed.  Value may be one of:
8583          * <pre><code>
8584  Roo.data.Record.EDIT
8585  Roo.data.Record.REJECT
8586  Roo.data.Record.COMMIT
8587          * </code></pre>
8588          */
8589         update : true,
8590         /**
8591          * @event clear
8592          * Fires when the data cache has been cleared.
8593          * @param {Store} this
8594          */
8595         clear : true,
8596         /**
8597          * @event beforeload
8598          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8599          * the load action will be canceled.
8600          * @param {Store} this
8601          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8602          */
8603         beforeload : true,
8604         /**
8605          * @event beforeloadadd
8606          * Fires after a new set of Records has been loaded.
8607          * @param {Store} this
8608          * @param {Roo.data.Record[]} records The Records that were loaded
8609          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8610          */
8611         beforeloadadd : true,
8612         /**
8613          * @event load
8614          * Fires after a new set of Records has been loaded, before they are added to the store.
8615          * @param {Store} this
8616          * @param {Roo.data.Record[]} records The Records that were loaded
8617          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8618          * @params {Object} return from reader
8619          */
8620         load : true,
8621         /**
8622          * @event loadexception
8623          * Fires if an exception occurs in the Proxy during loading.
8624          * Called with the signature of the Proxy's "loadexception" event.
8625          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8626          * 
8627          * @param {Proxy} 
8628          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8629          * @param {Object} load options 
8630          * @param {Object} jsonData from your request (normally this contains the Exception)
8631          */
8632         loadexception : true
8633     });
8634     
8635     if(this.proxy){
8636         this.proxy = Roo.factory(this.proxy, Roo.data);
8637         this.proxy.xmodule = this.xmodule || false;
8638         this.relayEvents(this.proxy,  ["loadexception"]);
8639     }
8640     this.sortToggle = {};
8641     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8642
8643     Roo.data.Store.superclass.constructor.call(this);
8644
8645     if(this.inlineData){
8646         this.loadData(this.inlineData);
8647         delete this.inlineData;
8648     }
8649 };
8650
8651 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8652      /**
8653     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8654     * without a remote query - used by combo/forms at present.
8655     */
8656     
8657     /**
8658     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8659     */
8660     /**
8661     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8662     */
8663     /**
8664     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8665     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8666     */
8667     /**
8668     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8669     * on any HTTP request
8670     */
8671     /**
8672     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8673     */
8674     /**
8675     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8676     */
8677     multiSort: false,
8678     /**
8679     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8680     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8681     */
8682     remoteSort : false,
8683
8684     /**
8685     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8686      * loaded or when a record is removed. (defaults to false).
8687     */
8688     pruneModifiedRecords : false,
8689
8690     // private
8691     lastOptions : null,
8692
8693     /**
8694      * Add Records to the Store and fires the add event.
8695      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8696      */
8697     add : function(records){
8698         records = [].concat(records);
8699         for(var i = 0, len = records.length; i < len; i++){
8700             records[i].join(this);
8701         }
8702         var index = this.data.length;
8703         this.data.addAll(records);
8704         this.fireEvent("add", this, records, index);
8705     },
8706
8707     /**
8708      * Remove a Record from the Store and fires the remove event.
8709      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8710      */
8711     remove : function(record){
8712         var index = this.data.indexOf(record);
8713         this.data.removeAt(index);
8714         if(this.pruneModifiedRecords){
8715             this.modified.remove(record);
8716         }
8717         this.fireEvent("remove", this, record, index);
8718     },
8719
8720     /**
8721      * Remove all Records from the Store and fires the clear event.
8722      */
8723     removeAll : function(){
8724         this.data.clear();
8725         if(this.pruneModifiedRecords){
8726             this.modified = [];
8727         }
8728         this.fireEvent("clear", this);
8729     },
8730
8731     /**
8732      * Inserts Records to the Store at the given index and fires the add event.
8733      * @param {Number} index The start index at which to insert the passed Records.
8734      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8735      */
8736     insert : function(index, records){
8737         records = [].concat(records);
8738         for(var i = 0, len = records.length; i < len; i++){
8739             this.data.insert(index, records[i]);
8740             records[i].join(this);
8741         }
8742         this.fireEvent("add", this, records, index);
8743     },
8744
8745     /**
8746      * Get the index within the cache of the passed Record.
8747      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8748      * @return {Number} The index of the passed Record. Returns -1 if not found.
8749      */
8750     indexOf : function(record){
8751         return this.data.indexOf(record);
8752     },
8753
8754     /**
8755      * Get the index within the cache of the Record with the passed id.
8756      * @param {String} id The id of the Record to find.
8757      * @return {Number} The index of the Record. Returns -1 if not found.
8758      */
8759     indexOfId : function(id){
8760         return this.data.indexOfKey(id);
8761     },
8762
8763     /**
8764      * Get the Record with the specified id.
8765      * @param {String} id The id of the Record to find.
8766      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8767      */
8768     getById : function(id){
8769         return this.data.key(id);
8770     },
8771
8772     /**
8773      * Get the Record at the specified index.
8774      * @param {Number} index The index of the Record to find.
8775      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8776      */
8777     getAt : function(index){
8778         return this.data.itemAt(index);
8779     },
8780
8781     /**
8782      * Returns a range of Records between specified indices.
8783      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8784      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8785      * @return {Roo.data.Record[]} An array of Records
8786      */
8787     getRange : function(start, end){
8788         return this.data.getRange(start, end);
8789     },
8790
8791     // private
8792     storeOptions : function(o){
8793         o = Roo.apply({}, o);
8794         delete o.callback;
8795         delete o.scope;
8796         this.lastOptions = o;
8797     },
8798
8799     /**
8800      * Loads the Record cache from the configured Proxy using the configured Reader.
8801      * <p>
8802      * If using remote paging, then the first load call must specify the <em>start</em>
8803      * and <em>limit</em> properties in the options.params property to establish the initial
8804      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8805      * <p>
8806      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8807      * and this call will return before the new data has been loaded. Perform any post-processing
8808      * in a callback function, or in a "load" event handler.</strong>
8809      * <p>
8810      * @param {Object} options An object containing properties which control loading options:<ul>
8811      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8812      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8813      * passed the following arguments:<ul>
8814      * <li>r : Roo.data.Record[]</li>
8815      * <li>options: Options object from the load call</li>
8816      * <li>success: Boolean success indicator</li></ul></li>
8817      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8818      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8819      * </ul>
8820      */
8821     load : function(options){
8822         options = options || {};
8823         if(this.fireEvent("beforeload", this, options) !== false){
8824             this.storeOptions(options);
8825             var p = Roo.apply(options.params || {}, this.baseParams);
8826             // if meta was not loaded from remote source.. try requesting it.
8827             if (!this.reader.metaFromRemote) {
8828                 p._requestMeta = 1;
8829             }
8830             if(this.sortInfo && this.remoteSort){
8831                 var pn = this.paramNames;
8832                 p[pn["sort"]] = this.sortInfo.field;
8833                 p[pn["dir"]] = this.sortInfo.direction;
8834             }
8835             if (this.multiSort) {
8836                 var pn = this.paramNames;
8837                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8838             }
8839             
8840             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8841         }
8842     },
8843
8844     /**
8845      * Reloads the Record cache from the configured Proxy using the configured Reader and
8846      * the options from the last load operation performed.
8847      * @param {Object} options (optional) An object containing properties which may override the options
8848      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8849      * the most recently used options are reused).
8850      */
8851     reload : function(options){
8852         this.load(Roo.applyIf(options||{}, this.lastOptions));
8853     },
8854
8855     // private
8856     // Called as a callback by the Reader during a load operation.
8857     loadRecords : function(o, options, success){
8858         if(!o || success === false){
8859             if(success !== false){
8860                 this.fireEvent("load", this, [], options, o);
8861             }
8862             if(options.callback){
8863                 options.callback.call(options.scope || this, [], options, false);
8864             }
8865             return;
8866         }
8867         // if data returned failure - throw an exception.
8868         if (o.success === false) {
8869             // show a message if no listener is registered.
8870             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8871                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8872             }
8873             // loadmask wil be hooked into this..
8874             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8875             return;
8876         }
8877         var r = o.records, t = o.totalRecords || r.length;
8878         
8879         this.fireEvent("beforeloadadd", this, r, options, o);
8880         
8881         if(!options || options.add !== true){
8882             if(this.pruneModifiedRecords){
8883                 this.modified = [];
8884             }
8885             for(var i = 0, len = r.length; i < len; i++){
8886                 r[i].join(this);
8887             }
8888             if(this.snapshot){
8889                 this.data = this.snapshot;
8890                 delete this.snapshot;
8891             }
8892             this.data.clear();
8893             this.data.addAll(r);
8894             this.totalLength = t;
8895             this.applySort();
8896             this.fireEvent("datachanged", this);
8897         }else{
8898             this.totalLength = Math.max(t, this.data.length+r.length);
8899             this.add(r);
8900         }
8901         this.fireEvent("load", this, r, options, o);
8902         if(options.callback){
8903             options.callback.call(options.scope || this, r, options, true);
8904         }
8905     },
8906
8907
8908     /**
8909      * Loads data from a passed data block. A Reader which understands the format of the data
8910      * must have been configured in the constructor.
8911      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8912      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8913      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8914      */
8915     loadData : function(o, append){
8916         var r = this.reader.readRecords(o);
8917         this.loadRecords(r, {add: append}, true);
8918     },
8919
8920     /**
8921      * Gets the number of cached records.
8922      * <p>
8923      * <em>If using paging, this may not be the total size of the dataset. If the data object
8924      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8925      * the data set size</em>
8926      */
8927     getCount : function(){
8928         return this.data.length || 0;
8929     },
8930
8931     /**
8932      * Gets the total number of records in the dataset as returned by the server.
8933      * <p>
8934      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8935      * the dataset size</em>
8936      */
8937     getTotalCount : function(){
8938         return this.totalLength || 0;
8939     },
8940
8941     /**
8942      * Returns the sort state of the Store as an object with two properties:
8943      * <pre><code>
8944  field {String} The name of the field by which the Records are sorted
8945  direction {String} The sort order, "ASC" or "DESC"
8946      * </code></pre>
8947      */
8948     getSortState : function(){
8949         return this.sortInfo;
8950     },
8951
8952     // private
8953     applySort : function(){
8954         if(this.sortInfo && !this.remoteSort){
8955             var s = this.sortInfo, f = s.field;
8956             var st = this.fields.get(f).sortType;
8957             var fn = function(r1, r2){
8958                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8959                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8960             };
8961             this.data.sort(s.direction, fn);
8962             if(this.snapshot && this.snapshot != this.data){
8963                 this.snapshot.sort(s.direction, fn);
8964             }
8965         }
8966     },
8967
8968     /**
8969      * Sets the default sort column and order to be used by the next load operation.
8970      * @param {String} fieldName The name of the field to sort by.
8971      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8972      */
8973     setDefaultSort : function(field, dir){
8974         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8975     },
8976
8977     /**
8978      * Sort the Records.
8979      * If remote sorting is used, the sort is performed on the server, and the cache is
8980      * reloaded. If local sorting is used, the cache is sorted internally.
8981      * @param {String} fieldName The name of the field to sort by.
8982      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8983      */
8984     sort : function(fieldName, dir){
8985         var f = this.fields.get(fieldName);
8986         if(!dir){
8987             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8988             
8989             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8990                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8991             }else{
8992                 dir = f.sortDir;
8993             }
8994         }
8995         this.sortToggle[f.name] = dir;
8996         this.sortInfo = {field: f.name, direction: dir};
8997         if(!this.remoteSort){
8998             this.applySort();
8999             this.fireEvent("datachanged", this);
9000         }else{
9001             this.load(this.lastOptions);
9002         }
9003     },
9004
9005     /**
9006      * Calls the specified function for each of the Records in the cache.
9007      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9008      * Returning <em>false</em> aborts and exits the iteration.
9009      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9010      */
9011     each : function(fn, scope){
9012         this.data.each(fn, scope);
9013     },
9014
9015     /**
9016      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9017      * (e.g., during paging).
9018      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9019      */
9020     getModifiedRecords : function(){
9021         return this.modified;
9022     },
9023
9024     // private
9025     createFilterFn : function(property, value, anyMatch){
9026         if(!value.exec){ // not a regex
9027             value = String(value);
9028             if(value.length == 0){
9029                 return false;
9030             }
9031             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9032         }
9033         return function(r){
9034             return value.test(r.data[property]);
9035         };
9036     },
9037
9038     /**
9039      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9040      * @param {String} property A field on your records
9041      * @param {Number} start The record index to start at (defaults to 0)
9042      * @param {Number} end The last record index to include (defaults to length - 1)
9043      * @return {Number} The sum
9044      */
9045     sum : function(property, start, end){
9046         var rs = this.data.items, v = 0;
9047         start = start || 0;
9048         end = (end || end === 0) ? end : rs.length-1;
9049
9050         for(var i = start; i <= end; i++){
9051             v += (rs[i].data[property] || 0);
9052         }
9053         return v;
9054     },
9055
9056     /**
9057      * Filter the records by a specified property.
9058      * @param {String} field A field on your records
9059      * @param {String/RegExp} value Either a string that the field
9060      * should start with or a RegExp to test against the field
9061      * @param {Boolean} anyMatch True to match any part not just the beginning
9062      */
9063     filter : function(property, value, anyMatch){
9064         var fn = this.createFilterFn(property, value, anyMatch);
9065         return fn ? this.filterBy(fn) : this.clearFilter();
9066     },
9067
9068     /**
9069      * Filter by a function. The specified function will be called with each
9070      * record in this data source. If the function returns true the record is included,
9071      * otherwise it is filtered.
9072      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9073      * @param {Object} scope (optional) The scope of the function (defaults to this)
9074      */
9075     filterBy : function(fn, scope){
9076         this.snapshot = this.snapshot || this.data;
9077         this.data = this.queryBy(fn, scope||this);
9078         this.fireEvent("datachanged", this);
9079     },
9080
9081     /**
9082      * Query the records by a specified property.
9083      * @param {String} field A field on your records
9084      * @param {String/RegExp} value Either a string that the field
9085      * should start with or a RegExp to test against the field
9086      * @param {Boolean} anyMatch True to match any part not just the beginning
9087      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9088      */
9089     query : function(property, value, anyMatch){
9090         var fn = this.createFilterFn(property, value, anyMatch);
9091         return fn ? this.queryBy(fn) : this.data.clone();
9092     },
9093
9094     /**
9095      * Query by a function. The specified function will be called with each
9096      * record in this data source. If the function returns true the record is included
9097      * in the results.
9098      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9099      * @param {Object} scope (optional) The scope of the function (defaults to this)
9100       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9101      **/
9102     queryBy : function(fn, scope){
9103         var data = this.snapshot || this.data;
9104         return data.filterBy(fn, scope||this);
9105     },
9106
9107     /**
9108      * Collects unique values for a particular dataIndex from this store.
9109      * @param {String} dataIndex The property to collect
9110      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9111      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9112      * @return {Array} An array of the unique values
9113      **/
9114     collect : function(dataIndex, allowNull, bypassFilter){
9115         var d = (bypassFilter === true && this.snapshot) ?
9116                 this.snapshot.items : this.data.items;
9117         var v, sv, r = [], l = {};
9118         for(var i = 0, len = d.length; i < len; i++){
9119             v = d[i].data[dataIndex];
9120             sv = String(v);
9121             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9122                 l[sv] = true;
9123                 r[r.length] = v;
9124             }
9125         }
9126         return r;
9127     },
9128
9129     /**
9130      * Revert to a view of the Record cache with no filtering applied.
9131      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9132      */
9133     clearFilter : function(suppressEvent){
9134         if(this.snapshot && this.snapshot != this.data){
9135             this.data = this.snapshot;
9136             delete this.snapshot;
9137             if(suppressEvent !== true){
9138                 this.fireEvent("datachanged", this);
9139             }
9140         }
9141     },
9142
9143     // private
9144     afterEdit : function(record){
9145         if(this.modified.indexOf(record) == -1){
9146             this.modified.push(record);
9147         }
9148         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9149     },
9150     
9151     // private
9152     afterReject : function(record){
9153         this.modified.remove(record);
9154         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9155     },
9156
9157     // private
9158     afterCommit : function(record){
9159         this.modified.remove(record);
9160         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9161     },
9162
9163     /**
9164      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9165      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9166      */
9167     commitChanges : function(){
9168         var m = this.modified.slice(0);
9169         this.modified = [];
9170         for(var i = 0, len = m.length; i < len; i++){
9171             m[i].commit();
9172         }
9173     },
9174
9175     /**
9176      * Cancel outstanding changes on all changed records.
9177      */
9178     rejectChanges : function(){
9179         var m = this.modified.slice(0);
9180         this.modified = [];
9181         for(var i = 0, len = m.length; i < len; i++){
9182             m[i].reject();
9183         }
9184     },
9185
9186     onMetaChange : function(meta, rtype, o){
9187         this.recordType = rtype;
9188         this.fields = rtype.prototype.fields;
9189         delete this.snapshot;
9190         this.sortInfo = meta.sortInfo || this.sortInfo;
9191         this.modified = [];
9192         this.fireEvent('metachange', this, this.reader.meta);
9193     },
9194     
9195     moveIndex : function(data, type)
9196     {
9197         var index = this.indexOf(data);
9198         
9199         var newIndex = index + type;
9200         
9201         this.remove(data);
9202         
9203         this.insert(newIndex, data);
9204         
9205     }
9206 });/*
9207  * Based on:
9208  * Ext JS Library 1.1.1
9209  * Copyright(c) 2006-2007, Ext JS, LLC.
9210  *
9211  * Originally Released Under LGPL - original licence link has changed is not relivant.
9212  *
9213  * Fork - LGPL
9214  * <script type="text/javascript">
9215  */
9216
9217 /**
9218  * @class Roo.data.SimpleStore
9219  * @extends Roo.data.Store
9220  * Small helper class to make creating Stores from Array data easier.
9221  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9222  * @cfg {Array} fields An array of field definition objects, or field name strings.
9223  * @cfg {Array} data The multi-dimensional array of data
9224  * @constructor
9225  * @param {Object} config
9226  */
9227 Roo.data.SimpleStore = function(config){
9228     Roo.data.SimpleStore.superclass.constructor.call(this, {
9229         isLocal : true,
9230         reader: new Roo.data.ArrayReader({
9231                 id: config.id
9232             },
9233             Roo.data.Record.create(config.fields)
9234         ),
9235         proxy : new Roo.data.MemoryProxy(config.data)
9236     });
9237     this.load();
9238 };
9239 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9240  * Based on:
9241  * Ext JS Library 1.1.1
9242  * Copyright(c) 2006-2007, Ext JS, LLC.
9243  *
9244  * Originally Released Under LGPL - original licence link has changed is not relivant.
9245  *
9246  * Fork - LGPL
9247  * <script type="text/javascript">
9248  */
9249
9250 /**
9251 /**
9252  * @extends Roo.data.Store
9253  * @class Roo.data.JsonStore
9254  * Small helper class to make creating Stores for JSON data easier. <br/>
9255 <pre><code>
9256 var store = new Roo.data.JsonStore({
9257     url: 'get-images.php',
9258     root: 'images',
9259     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9260 });
9261 </code></pre>
9262  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9263  * JsonReader and HttpProxy (unless inline data is provided).</b>
9264  * @cfg {Array} fields An array of field definition objects, or field name strings.
9265  * @constructor
9266  * @param {Object} config
9267  */
9268 Roo.data.JsonStore = function(c){
9269     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9270         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9271         reader: new Roo.data.JsonReader(c, c.fields)
9272     }));
9273 };
9274 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9275  * Based on:
9276  * Ext JS Library 1.1.1
9277  * Copyright(c) 2006-2007, Ext JS, LLC.
9278  *
9279  * Originally Released Under LGPL - original licence link has changed is not relivant.
9280  *
9281  * Fork - LGPL
9282  * <script type="text/javascript">
9283  */
9284
9285  
9286 Roo.data.Field = function(config){
9287     if(typeof config == "string"){
9288         config = {name: config};
9289     }
9290     Roo.apply(this, config);
9291     
9292     if(!this.type){
9293         this.type = "auto";
9294     }
9295     
9296     var st = Roo.data.SortTypes;
9297     // named sortTypes are supported, here we look them up
9298     if(typeof this.sortType == "string"){
9299         this.sortType = st[this.sortType];
9300     }
9301     
9302     // set default sortType for strings and dates
9303     if(!this.sortType){
9304         switch(this.type){
9305             case "string":
9306                 this.sortType = st.asUCString;
9307                 break;
9308             case "date":
9309                 this.sortType = st.asDate;
9310                 break;
9311             default:
9312                 this.sortType = st.none;
9313         }
9314     }
9315
9316     // define once
9317     var stripRe = /[\$,%]/g;
9318
9319     // prebuilt conversion function for this field, instead of
9320     // switching every time we're reading a value
9321     if(!this.convert){
9322         var cv, dateFormat = this.dateFormat;
9323         switch(this.type){
9324             case "":
9325             case "auto":
9326             case undefined:
9327                 cv = function(v){ return v; };
9328                 break;
9329             case "string":
9330                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9331                 break;
9332             case "int":
9333                 cv = function(v){
9334                     return v !== undefined && v !== null && v !== '' ?
9335                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9336                     };
9337                 break;
9338             case "float":
9339                 cv = function(v){
9340                     return v !== undefined && v !== null && v !== '' ?
9341                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9342                     };
9343                 break;
9344             case "bool":
9345             case "boolean":
9346                 cv = function(v){ return v === true || v === "true" || v == 1; };
9347                 break;
9348             case "date":
9349                 cv = function(v){
9350                     if(!v){
9351                         return '';
9352                     }
9353                     if(v instanceof Date){
9354                         return v;
9355                     }
9356                     if(dateFormat){
9357                         if(dateFormat == "timestamp"){
9358                             return new Date(v*1000);
9359                         }
9360                         return Date.parseDate(v, dateFormat);
9361                     }
9362                     var parsed = Date.parse(v);
9363                     return parsed ? new Date(parsed) : null;
9364                 };
9365              break;
9366             
9367         }
9368         this.convert = cv;
9369     }
9370 };
9371
9372 Roo.data.Field.prototype = {
9373     dateFormat: null,
9374     defaultValue: "",
9375     mapping: null,
9376     sortType : null,
9377     sortDir : "ASC"
9378 };/*
9379  * Based on:
9380  * Ext JS Library 1.1.1
9381  * Copyright(c) 2006-2007, Ext JS, LLC.
9382  *
9383  * Originally Released Under LGPL - original licence link has changed is not relivant.
9384  *
9385  * Fork - LGPL
9386  * <script type="text/javascript">
9387  */
9388  
9389 // Base class for reading structured data from a data source.  This class is intended to be
9390 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9391
9392 /**
9393  * @class Roo.data.DataReader
9394  * Base class for reading structured data from a data source.  This class is intended to be
9395  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9396  */
9397
9398 Roo.data.DataReader = function(meta, recordType){
9399     
9400     this.meta = meta;
9401     
9402     this.recordType = recordType instanceof Array ? 
9403         Roo.data.Record.create(recordType) : recordType;
9404 };
9405
9406 Roo.data.DataReader.prototype = {
9407      /**
9408      * Create an empty record
9409      * @param {Object} data (optional) - overlay some values
9410      * @return {Roo.data.Record} record created.
9411      */
9412     newRow :  function(d) {
9413         var da =  {};
9414         this.recordType.prototype.fields.each(function(c) {
9415             switch( c.type) {
9416                 case 'int' : da[c.name] = 0; break;
9417                 case 'date' : da[c.name] = new Date(); break;
9418                 case 'float' : da[c.name] = 0.0; break;
9419                 case 'boolean' : da[c.name] = false; break;
9420                 default : da[c.name] = ""; break;
9421             }
9422             
9423         });
9424         return new this.recordType(Roo.apply(da, d));
9425     }
9426     
9427 };/*
9428  * Based on:
9429  * Ext JS Library 1.1.1
9430  * Copyright(c) 2006-2007, Ext JS, LLC.
9431  *
9432  * Originally Released Under LGPL - original licence link has changed is not relivant.
9433  *
9434  * Fork - LGPL
9435  * <script type="text/javascript">
9436  */
9437
9438 /**
9439  * @class Roo.data.DataProxy
9440  * @extends Roo.data.Observable
9441  * This class is an abstract base class for implementations which provide retrieval of
9442  * unformatted data objects.<br>
9443  * <p>
9444  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9445  * (of the appropriate type which knows how to parse the data object) to provide a block of
9446  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9447  * <p>
9448  * Custom implementations must implement the load method as described in
9449  * {@link Roo.data.HttpProxy#load}.
9450  */
9451 Roo.data.DataProxy = function(){
9452     this.addEvents({
9453         /**
9454          * @event beforeload
9455          * Fires before a network request is made to retrieve a data object.
9456          * @param {Object} This DataProxy object.
9457          * @param {Object} params The params parameter to the load function.
9458          */
9459         beforeload : true,
9460         /**
9461          * @event load
9462          * Fires before the load method's callback is called.
9463          * @param {Object} This DataProxy object.
9464          * @param {Object} o The data object.
9465          * @param {Object} arg The callback argument object passed to the load function.
9466          */
9467         load : true,
9468         /**
9469          * @event loadexception
9470          * Fires if an Exception occurs during data retrieval.
9471          * @param {Object} This DataProxy object.
9472          * @param {Object} o The data object.
9473          * @param {Object} arg The callback argument object passed to the load function.
9474          * @param {Object} e The Exception.
9475          */
9476         loadexception : true
9477     });
9478     Roo.data.DataProxy.superclass.constructor.call(this);
9479 };
9480
9481 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9482
9483     /**
9484      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9485      */
9486 /*
9487  * Based on:
9488  * Ext JS Library 1.1.1
9489  * Copyright(c) 2006-2007, Ext JS, LLC.
9490  *
9491  * Originally Released Under LGPL - original licence link has changed is not relivant.
9492  *
9493  * Fork - LGPL
9494  * <script type="text/javascript">
9495  */
9496 /**
9497  * @class Roo.data.MemoryProxy
9498  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9499  * to the Reader when its load method is called.
9500  * @constructor
9501  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9502  */
9503 Roo.data.MemoryProxy = function(data){
9504     if (data.data) {
9505         data = data.data;
9506     }
9507     Roo.data.MemoryProxy.superclass.constructor.call(this);
9508     this.data = data;
9509 };
9510
9511 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9512     /**
9513      * Load data from the requested source (in this case an in-memory
9514      * data object passed to the constructor), read the data object into
9515      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9516      * process that block using the passed callback.
9517      * @param {Object} params This parameter is not used by the MemoryProxy class.
9518      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9519      * object into a block of Roo.data.Records.
9520      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9521      * The function must be passed <ul>
9522      * <li>The Record block object</li>
9523      * <li>The "arg" argument from the load function</li>
9524      * <li>A boolean success indicator</li>
9525      * </ul>
9526      * @param {Object} scope The scope in which to call the callback
9527      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9528      */
9529     load : function(params, reader, callback, scope, arg){
9530         params = params || {};
9531         var result;
9532         try {
9533             result = reader.readRecords(this.data);
9534         }catch(e){
9535             this.fireEvent("loadexception", this, arg, null, e);
9536             callback.call(scope, null, arg, false);
9537             return;
9538         }
9539         callback.call(scope, result, arg, true);
9540     },
9541     
9542     // private
9543     update : function(params, records){
9544         
9545     }
9546 });/*
9547  * Based on:
9548  * Ext JS Library 1.1.1
9549  * Copyright(c) 2006-2007, Ext JS, LLC.
9550  *
9551  * Originally Released Under LGPL - original licence link has changed is not relivant.
9552  *
9553  * Fork - LGPL
9554  * <script type="text/javascript">
9555  */
9556 /**
9557  * @class Roo.data.HttpProxy
9558  * @extends Roo.data.DataProxy
9559  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9560  * configured to reference a certain URL.<br><br>
9561  * <p>
9562  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9563  * from which the running page was served.<br><br>
9564  * <p>
9565  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9566  * <p>
9567  * Be aware that to enable the browser to parse an XML document, the server must set
9568  * the Content-Type header in the HTTP response to "text/xml".
9569  * @constructor
9570  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9571  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9572  * will be used to make the request.
9573  */
9574 Roo.data.HttpProxy = function(conn){
9575     Roo.data.HttpProxy.superclass.constructor.call(this);
9576     // is conn a conn config or a real conn?
9577     this.conn = conn;
9578     this.useAjax = !conn || !conn.events;
9579   
9580 };
9581
9582 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9583     // thse are take from connection...
9584     
9585     /**
9586      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9587      */
9588     /**
9589      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9590      * extra parameters to each request made by this object. (defaults to undefined)
9591      */
9592     /**
9593      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9594      *  to each request made by this object. (defaults to undefined)
9595      */
9596     /**
9597      * @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)
9598      */
9599     /**
9600      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9601      */
9602      /**
9603      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9604      * @type Boolean
9605      */
9606   
9607
9608     /**
9609      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9610      * @type Boolean
9611      */
9612     /**
9613      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9614      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9615      * a finer-grained basis than the DataProxy events.
9616      */
9617     getConnection : function(){
9618         return this.useAjax ? Roo.Ajax : this.conn;
9619     },
9620
9621     /**
9622      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9623      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9624      * process that block using the passed callback.
9625      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9626      * for the request to the remote server.
9627      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9628      * object into a block of Roo.data.Records.
9629      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9630      * The function must be passed <ul>
9631      * <li>The Record block object</li>
9632      * <li>The "arg" argument from the load function</li>
9633      * <li>A boolean success indicator</li>
9634      * </ul>
9635      * @param {Object} scope The scope in which to call the callback
9636      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9637      */
9638     load : function(params, reader, callback, scope, arg){
9639         if(this.fireEvent("beforeload", this, params) !== false){
9640             var  o = {
9641                 params : params || {},
9642                 request: {
9643                     callback : callback,
9644                     scope : scope,
9645                     arg : arg
9646                 },
9647                 reader: reader,
9648                 callback : this.loadResponse,
9649                 scope: this
9650             };
9651             if(this.useAjax){
9652                 Roo.applyIf(o, this.conn);
9653                 if(this.activeRequest){
9654                     Roo.Ajax.abort(this.activeRequest);
9655                 }
9656                 this.activeRequest = Roo.Ajax.request(o);
9657             }else{
9658                 this.conn.request(o);
9659             }
9660         }else{
9661             callback.call(scope||this, null, arg, false);
9662         }
9663     },
9664
9665     // private
9666     loadResponse : function(o, success, response){
9667         delete this.activeRequest;
9668         if(!success){
9669             this.fireEvent("loadexception", this, o, response);
9670             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9671             return;
9672         }
9673         var result;
9674         try {
9675             result = o.reader.read(response);
9676         }catch(e){
9677             this.fireEvent("loadexception", this, o, response, e);
9678             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9679             return;
9680         }
9681         
9682         this.fireEvent("load", this, o, o.request.arg);
9683         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9684     },
9685
9686     // private
9687     update : function(dataSet){
9688
9689     },
9690
9691     // private
9692     updateResponse : function(dataSet){
9693
9694     }
9695 });/*
9696  * Based on:
9697  * Ext JS Library 1.1.1
9698  * Copyright(c) 2006-2007, Ext JS, LLC.
9699  *
9700  * Originally Released Under LGPL - original licence link has changed is not relivant.
9701  *
9702  * Fork - LGPL
9703  * <script type="text/javascript">
9704  */
9705
9706 /**
9707  * @class Roo.data.ScriptTagProxy
9708  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9709  * other than the originating domain of the running page.<br><br>
9710  * <p>
9711  * <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
9712  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9713  * <p>
9714  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9715  * source code that is used as the source inside a &lt;script> tag.<br><br>
9716  * <p>
9717  * In order for the browser to process the returned data, the server must wrap the data object
9718  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9719  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9720  * depending on whether the callback name was passed:
9721  * <p>
9722  * <pre><code>
9723 boolean scriptTag = false;
9724 String cb = request.getParameter("callback");
9725 if (cb != null) {
9726     scriptTag = true;
9727     response.setContentType("text/javascript");
9728 } else {
9729     response.setContentType("application/x-json");
9730 }
9731 Writer out = response.getWriter();
9732 if (scriptTag) {
9733     out.write(cb + "(");
9734 }
9735 out.print(dataBlock.toJsonString());
9736 if (scriptTag) {
9737     out.write(");");
9738 }
9739 </pre></code>
9740  *
9741  * @constructor
9742  * @param {Object} config A configuration object.
9743  */
9744 Roo.data.ScriptTagProxy = function(config){
9745     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9746     Roo.apply(this, config);
9747     this.head = document.getElementsByTagName("head")[0];
9748 };
9749
9750 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9751
9752 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9753     /**
9754      * @cfg {String} url The URL from which to request the data object.
9755      */
9756     /**
9757      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9758      */
9759     timeout : 30000,
9760     /**
9761      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9762      * the server the name of the callback function set up by the load call to process the returned data object.
9763      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9764      * javascript output which calls this named function passing the data object as its only parameter.
9765      */
9766     callbackParam : "callback",
9767     /**
9768      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9769      * name to the request.
9770      */
9771     nocache : true,
9772
9773     /**
9774      * Load data from the configured URL, read the data object into
9775      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9776      * process that block using the passed callback.
9777      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9778      * for the request to the remote server.
9779      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9780      * object into a block of Roo.data.Records.
9781      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9782      * The function must be passed <ul>
9783      * <li>The Record block object</li>
9784      * <li>The "arg" argument from the load function</li>
9785      * <li>A boolean success indicator</li>
9786      * </ul>
9787      * @param {Object} scope The scope in which to call the callback
9788      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9789      */
9790     load : function(params, reader, callback, scope, arg){
9791         if(this.fireEvent("beforeload", this, params) !== false){
9792
9793             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9794
9795             var url = this.url;
9796             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9797             if(this.nocache){
9798                 url += "&_dc=" + (new Date().getTime());
9799             }
9800             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9801             var trans = {
9802                 id : transId,
9803                 cb : "stcCallback"+transId,
9804                 scriptId : "stcScript"+transId,
9805                 params : params,
9806                 arg : arg,
9807                 url : url,
9808                 callback : callback,
9809                 scope : scope,
9810                 reader : reader
9811             };
9812             var conn = this;
9813
9814             window[trans.cb] = function(o){
9815                 conn.handleResponse(o, trans);
9816             };
9817
9818             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9819
9820             if(this.autoAbort !== false){
9821                 this.abort();
9822             }
9823
9824             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9825
9826             var script = document.createElement("script");
9827             script.setAttribute("src", url);
9828             script.setAttribute("type", "text/javascript");
9829             script.setAttribute("id", trans.scriptId);
9830             this.head.appendChild(script);
9831
9832             this.trans = trans;
9833         }else{
9834             callback.call(scope||this, null, arg, false);
9835         }
9836     },
9837
9838     // private
9839     isLoading : function(){
9840         return this.trans ? true : false;
9841     },
9842
9843     /**
9844      * Abort the current server request.
9845      */
9846     abort : function(){
9847         if(this.isLoading()){
9848             this.destroyTrans(this.trans);
9849         }
9850     },
9851
9852     // private
9853     destroyTrans : function(trans, isLoaded){
9854         this.head.removeChild(document.getElementById(trans.scriptId));
9855         clearTimeout(trans.timeoutId);
9856         if(isLoaded){
9857             window[trans.cb] = undefined;
9858             try{
9859                 delete window[trans.cb];
9860             }catch(e){}
9861         }else{
9862             // if hasn't been loaded, wait for load to remove it to prevent script error
9863             window[trans.cb] = function(){
9864                 window[trans.cb] = undefined;
9865                 try{
9866                     delete window[trans.cb];
9867                 }catch(e){}
9868             };
9869         }
9870     },
9871
9872     // private
9873     handleResponse : function(o, trans){
9874         this.trans = false;
9875         this.destroyTrans(trans, true);
9876         var result;
9877         try {
9878             result = trans.reader.readRecords(o);
9879         }catch(e){
9880             this.fireEvent("loadexception", this, o, trans.arg, e);
9881             trans.callback.call(trans.scope||window, null, trans.arg, false);
9882             return;
9883         }
9884         this.fireEvent("load", this, o, trans.arg);
9885         trans.callback.call(trans.scope||window, result, trans.arg, true);
9886     },
9887
9888     // private
9889     handleFailure : function(trans){
9890         this.trans = false;
9891         this.destroyTrans(trans, false);
9892         this.fireEvent("loadexception", this, null, trans.arg);
9893         trans.callback.call(trans.scope||window, null, trans.arg, false);
9894     }
9895 });/*
9896  * Based on:
9897  * Ext JS Library 1.1.1
9898  * Copyright(c) 2006-2007, Ext JS, LLC.
9899  *
9900  * Originally Released Under LGPL - original licence link has changed is not relivant.
9901  *
9902  * Fork - LGPL
9903  * <script type="text/javascript">
9904  */
9905
9906 /**
9907  * @class Roo.data.JsonReader
9908  * @extends Roo.data.DataReader
9909  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9910  * based on mappings in a provided Roo.data.Record constructor.
9911  * 
9912  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9913  * in the reply previously. 
9914  * 
9915  * <p>
9916  * Example code:
9917  * <pre><code>
9918 var RecordDef = Roo.data.Record.create([
9919     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9920     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9921 ]);
9922 var myReader = new Roo.data.JsonReader({
9923     totalProperty: "results",    // The property which contains the total dataset size (optional)
9924     root: "rows",                // The property which contains an Array of row objects
9925     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9926 }, RecordDef);
9927 </code></pre>
9928  * <p>
9929  * This would consume a JSON file like this:
9930  * <pre><code>
9931 { 'results': 2, 'rows': [
9932     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9933     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9934 }
9935 </code></pre>
9936  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9937  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9938  * paged from the remote server.
9939  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9940  * @cfg {String} root name of the property which contains the Array of row objects.
9941  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9942  * @constructor
9943  * Create a new JsonReader
9944  * @param {Object} meta Metadata configuration options
9945  * @param {Object} recordType Either an Array of field definition objects,
9946  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9947  */
9948 Roo.data.JsonReader = function(meta, recordType){
9949     
9950     meta = meta || {};
9951     // set some defaults:
9952     Roo.applyIf(meta, {
9953         totalProperty: 'total',
9954         successProperty : 'success',
9955         root : 'data',
9956         id : 'id'
9957     });
9958     
9959     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9960 };
9961 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9962     
9963     /**
9964      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9965      * Used by Store query builder to append _requestMeta to params.
9966      * 
9967      */
9968     metaFromRemote : false,
9969     /**
9970      * This method is only used by a DataProxy which has retrieved data from a remote server.
9971      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9972      * @return {Object} data A data block which is used by an Roo.data.Store object as
9973      * a cache of Roo.data.Records.
9974      */
9975     read : function(response){
9976         var json = response.responseText;
9977        
9978         var o = /* eval:var:o */ eval("("+json+")");
9979         if(!o) {
9980             throw {message: "JsonReader.read: Json object not found"};
9981         }
9982         
9983         if(o.metaData){
9984             
9985             delete this.ef;
9986             this.metaFromRemote = true;
9987             this.meta = o.metaData;
9988             this.recordType = Roo.data.Record.create(o.metaData.fields);
9989             this.onMetaChange(this.meta, this.recordType, o);
9990         }
9991         return this.readRecords(o);
9992     },
9993
9994     // private function a store will implement
9995     onMetaChange : function(meta, recordType, o){
9996
9997     },
9998
9999     /**
10000          * @ignore
10001          */
10002     simpleAccess: function(obj, subsc) {
10003         return obj[subsc];
10004     },
10005
10006         /**
10007          * @ignore
10008          */
10009     getJsonAccessor: function(){
10010         var re = /[\[\.]/;
10011         return function(expr) {
10012             try {
10013                 return(re.test(expr))
10014                     ? new Function("obj", "return obj." + expr)
10015                     : function(obj){
10016                         return obj[expr];
10017                     };
10018             } catch(e){}
10019             return Roo.emptyFn;
10020         };
10021     }(),
10022
10023     /**
10024      * Create a data block containing Roo.data.Records from an XML document.
10025      * @param {Object} o An object which contains an Array of row objects in the property specified
10026      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10027      * which contains the total size of the dataset.
10028      * @return {Object} data A data block which is used by an Roo.data.Store object as
10029      * a cache of Roo.data.Records.
10030      */
10031     readRecords : function(o){
10032         /**
10033          * After any data loads, the raw JSON data is available for further custom processing.
10034          * @type Object
10035          */
10036         this.o = o;
10037         var s = this.meta, Record = this.recordType,
10038             f = Record.prototype.fields, fi = f.items, fl = f.length;
10039
10040 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10041         if (!this.ef) {
10042             if(s.totalProperty) {
10043                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10044                 }
10045                 if(s.successProperty) {
10046                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10047                 }
10048                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10049                 if (s.id) {
10050                         var g = this.getJsonAccessor(s.id);
10051                         this.getId = function(rec) {
10052                                 var r = g(rec);
10053                                 return (r === undefined || r === "") ? null : r;
10054                         };
10055                 } else {
10056                         this.getId = function(){return null;};
10057                 }
10058             this.ef = [];
10059             for(var jj = 0; jj < fl; jj++){
10060                 f = fi[jj];
10061                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10062                 this.ef[jj] = this.getJsonAccessor(map);
10063             }
10064         }
10065
10066         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10067         if(s.totalProperty){
10068             var vt = parseInt(this.getTotal(o), 10);
10069             if(!isNaN(vt)){
10070                 totalRecords = vt;
10071             }
10072         }
10073         if(s.successProperty){
10074             var vs = this.getSuccess(o);
10075             if(vs === false || vs === 'false'){
10076                 success = false;
10077             }
10078         }
10079         var records = [];
10080             for(var i = 0; i < c; i++){
10081                     var n = root[i];
10082                 var values = {};
10083                 var id = this.getId(n);
10084                 for(var j = 0; j < fl; j++){
10085                     f = fi[j];
10086                 var v = this.ef[j](n);
10087                 if (!f.convert) {
10088                     Roo.log('missing convert for ' + f.name);
10089                     Roo.log(f);
10090                     continue;
10091                 }
10092                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10093                 }
10094                 var record = new Record(values, id);
10095                 record.json = n;
10096                 records[i] = record;
10097             }
10098             return {
10099             raw : o,
10100                 success : success,
10101                 records : records,
10102                 totalRecords : totalRecords
10103             };
10104     }
10105 });/*
10106  * Based on:
10107  * Ext JS Library 1.1.1
10108  * Copyright(c) 2006-2007, Ext JS, LLC.
10109  *
10110  * Originally Released Under LGPL - original licence link has changed is not relivant.
10111  *
10112  * Fork - LGPL
10113  * <script type="text/javascript">
10114  */
10115
10116 /**
10117  * @class Roo.data.ArrayReader
10118  * @extends Roo.data.DataReader
10119  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10120  * Each element of that Array represents a row of data fields. The
10121  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10122  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10123  * <p>
10124  * Example code:.
10125  * <pre><code>
10126 var RecordDef = Roo.data.Record.create([
10127     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10128     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10129 ]);
10130 var myReader = new Roo.data.ArrayReader({
10131     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10132 }, RecordDef);
10133 </code></pre>
10134  * <p>
10135  * This would consume an Array like this:
10136  * <pre><code>
10137 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10138   </code></pre>
10139  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10140  * @constructor
10141  * Create a new JsonReader
10142  * @param {Object} meta Metadata configuration options.
10143  * @param {Object} recordType Either an Array of field definition objects
10144  * as specified to {@link Roo.data.Record#create},
10145  * or an {@link Roo.data.Record} object
10146  * created using {@link Roo.data.Record#create}.
10147  */
10148 Roo.data.ArrayReader = function(meta, recordType){
10149     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10150 };
10151
10152 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10153     /**
10154      * Create a data block containing Roo.data.Records from an XML document.
10155      * @param {Object} o An Array of row objects which represents the dataset.
10156      * @return {Object} data A data block which is used by an Roo.data.Store object as
10157      * a cache of Roo.data.Records.
10158      */
10159     readRecords : function(o){
10160         var sid = this.meta ? this.meta.id : null;
10161         var recordType = this.recordType, fields = recordType.prototype.fields;
10162         var records = [];
10163         var root = o;
10164             for(var i = 0; i < root.length; i++){
10165                     var n = root[i];
10166                 var values = {};
10167                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10168                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10169                 var f = fields.items[j];
10170                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10171                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10172                 v = f.convert(v);
10173                 values[f.name] = v;
10174             }
10175                 var record = new recordType(values, id);
10176                 record.json = n;
10177                 records[records.length] = record;
10178             }
10179             return {
10180                 records : records,
10181                 totalRecords : records.length
10182             };
10183     }
10184 });/*
10185  * - LGPL
10186  * * 
10187  */
10188
10189 /**
10190  * @class Roo.bootstrap.ComboBox
10191  * @extends Roo.bootstrap.TriggerField
10192  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10193  * @cfg {Boolean} append (true|false) default false
10194  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10195  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10196  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10197  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10198  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10199  * @constructor
10200  * Create a new ComboBox.
10201  * @param {Object} config Configuration options
10202  */
10203 Roo.bootstrap.ComboBox = function(config){
10204     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10205     this.addEvents({
10206         /**
10207          * @event expand
10208          * Fires when the dropdown list is expanded
10209              * @param {Roo.bootstrap.ComboBox} combo This combo box
10210              */
10211         'expand' : true,
10212         /**
10213          * @event collapse
10214          * Fires when the dropdown list is collapsed
10215              * @param {Roo.bootstrap.ComboBox} combo This combo box
10216              */
10217         'collapse' : true,
10218         /**
10219          * @event beforeselect
10220          * Fires before a list item is selected. Return false to cancel the selection.
10221              * @param {Roo.bootstrap.ComboBox} combo This combo box
10222              * @param {Roo.data.Record} record The data record returned from the underlying store
10223              * @param {Number} index The index of the selected item in the dropdown list
10224              */
10225         'beforeselect' : true,
10226         /**
10227          * @event select
10228          * Fires when a list item is selected
10229              * @param {Roo.bootstrap.ComboBox} combo This combo box
10230              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10231              * @param {Number} index The index of the selected item in the dropdown list
10232              */
10233         'select' : true,
10234         /**
10235          * @event beforequery
10236          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10237          * The event object passed has these properties:
10238              * @param {Roo.bootstrap.ComboBox} combo This combo box
10239              * @param {String} query The query
10240              * @param {Boolean} forceAll true to force "all" query
10241              * @param {Boolean} cancel true to cancel the query
10242              * @param {Object} e The query event object
10243              */
10244         'beforequery': true,
10245          /**
10246          * @event add
10247          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10248              * @param {Roo.bootstrap.ComboBox} combo This combo box
10249              */
10250         'add' : true,
10251         /**
10252          * @event edit
10253          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10254              * @param {Roo.bootstrap.ComboBox} combo This combo box
10255              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10256              */
10257         'edit' : true,
10258         /**
10259          * @event remove
10260          * Fires when the remove value from the combobox array
10261              * @param {Roo.bootstrap.ComboBox} combo This combo box
10262              */
10263         'remove' : true
10264         
10265     });
10266     
10267     this.item = [];
10268     this.tickItems = [];
10269     
10270     this.selectedIndex = -1;
10271     if(this.mode == 'local'){
10272         if(config.queryDelay === undefined){
10273             this.queryDelay = 10;
10274         }
10275         if(config.minChars === undefined){
10276             this.minChars = 0;
10277         }
10278     }
10279 };
10280
10281 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10282      
10283     /**
10284      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10285      * rendering into an Roo.Editor, defaults to false)
10286      */
10287     /**
10288      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10289      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10290      */
10291     /**
10292      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10293      */
10294     /**
10295      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10296      * the dropdown list (defaults to undefined, with no header element)
10297      */
10298
10299      /**
10300      * @cfg {String/Roo.Template} tpl The template to use to render the output
10301      */
10302      
10303      /**
10304      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10305      */
10306     listWidth: undefined,
10307     /**
10308      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10309      * mode = 'remote' or 'text' if mode = 'local')
10310      */
10311     displayField: undefined,
10312     /**
10313      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10314      * mode = 'remote' or 'value' if mode = 'local'). 
10315      * Note: use of a valueField requires the user make a selection
10316      * in order for a value to be mapped.
10317      */
10318     valueField: undefined,
10319     
10320     
10321     /**
10322      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10323      * field's data value (defaults to the underlying DOM element's name)
10324      */
10325     hiddenName: undefined,
10326     /**
10327      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10328      */
10329     listClass: '',
10330     /**
10331      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10332      */
10333     selectedClass: 'active',
10334     
10335     /**
10336      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10337      */
10338     shadow:'sides',
10339     /**
10340      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10341      * anchor positions (defaults to 'tl-bl')
10342      */
10343     listAlign: 'tl-bl?',
10344     /**
10345      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10346      */
10347     maxHeight: 300,
10348     /**
10349      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10350      * query specified by the allQuery config option (defaults to 'query')
10351      */
10352     triggerAction: 'query',
10353     /**
10354      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10355      * (defaults to 4, does not apply if editable = false)
10356      */
10357     minChars : 4,
10358     /**
10359      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10360      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10361      */
10362     typeAhead: false,
10363     /**
10364      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10365      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10366      */
10367     queryDelay: 500,
10368     /**
10369      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10370      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10371      */
10372     pageSize: 0,
10373     /**
10374      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10375      * when editable = true (defaults to false)
10376      */
10377     selectOnFocus:false,
10378     /**
10379      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10380      */
10381     queryParam: 'query',
10382     /**
10383      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10384      * when mode = 'remote' (defaults to 'Loading...')
10385      */
10386     loadingText: 'Loading...',
10387     /**
10388      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10389      */
10390     resizable: false,
10391     /**
10392      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10393      */
10394     handleHeight : 8,
10395     /**
10396      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10397      * traditional select (defaults to true)
10398      */
10399     editable: true,
10400     /**
10401      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10402      */
10403     allQuery: '',
10404     /**
10405      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10406      */
10407     mode: 'remote',
10408     /**
10409      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10410      * listWidth has a higher value)
10411      */
10412     minListWidth : 70,
10413     /**
10414      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10415      * allow the user to set arbitrary text into the field (defaults to false)
10416      */
10417     forceSelection:false,
10418     /**
10419      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10420      * if typeAhead = true (defaults to 250)
10421      */
10422     typeAheadDelay : 250,
10423     /**
10424      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10425      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10426      */
10427     valueNotFoundText : undefined,
10428     /**
10429      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10430      */
10431     blockFocus : false,
10432     
10433     /**
10434      * @cfg {Boolean} disableClear Disable showing of clear button.
10435      */
10436     disableClear : false,
10437     /**
10438      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10439      */
10440     alwaysQuery : false,
10441     
10442     /**
10443      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10444      */
10445     multiple : false,
10446     
10447     //private
10448     addicon : false,
10449     editicon: false,
10450     
10451     page: 0,
10452     hasQuery: false,
10453     append: false,
10454     loadNext: false,
10455     autoFocus : true,
10456     tickable : false,
10457     btnPosition : 'right',
10458     triggerList : true,
10459     showToggleBtn : true,
10460     // element that contains real text value.. (when hidden is used..)
10461     
10462     getAutoCreate : function()
10463     {
10464         var cfg = false;
10465         
10466         /*
10467          *  Normal ComboBox
10468          */
10469         if(!this.tickable){
10470             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10471             return cfg;
10472         }
10473         
10474         /*
10475          *  ComboBox with tickable selections
10476          */
10477              
10478         var align = this.labelAlign || this.parentLabelAlign();
10479         
10480         cfg = {
10481             cls : 'form-group roo-combobox-tickable' //input-group
10482         };
10483         
10484         
10485         var buttons = {
10486             tag : 'div',
10487             cls : 'tickable-buttons',
10488             cn : [
10489                 {
10490                     tag : 'button',
10491                     type : 'button',
10492                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10493                     html : 'Edit'
10494                 },
10495                 {
10496                     tag : 'button',
10497                     type : 'button',
10498                     name : 'ok',
10499                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10500                     html : 'Done'
10501                 },
10502                 {
10503                     tag : 'button',
10504                     type : 'button',
10505                     name : 'cancel',
10506                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10507                     html : 'Cancel'
10508                 }
10509             ]
10510         };
10511         
10512         var _this = this;
10513         Roo.each(buttons.cn, function(c){
10514             if (_this.size) {
10515                 c.cls += ' btn-' + _this.size;
10516             }
10517
10518             if (_this.disabled) {
10519                 c.disabled = true;
10520             }
10521         });
10522         
10523         var box = {
10524             tag: 'div',
10525             cn: [
10526                 {
10527                     tag: 'input',
10528                     type : 'hidden',
10529                     cls: 'form-hidden-field'
10530                 },
10531                 {
10532                     tag: 'ul',
10533                     cls: 'select2-choices',
10534                     cn:[
10535                         {
10536                             tag: 'li',
10537                             cls: 'select2-search-field',
10538                             cn: [
10539
10540                                 buttons
10541                             ]
10542                         }
10543                     ]
10544                 }
10545             ]
10546         }
10547         
10548         var combobox = {
10549             cls: 'select2-container input-group select2-container-multi',
10550             cn: [
10551                 box
10552 //                {
10553 //                    tag: 'ul',
10554 //                    cls: 'typeahead typeahead-long dropdown-menu',
10555 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10556 //                }
10557             ]
10558         };
10559         
10560         if (align ==='left' && this.fieldLabel.length) {
10561             
10562                 Roo.log("left and has label");
10563                 cfg.cn = [
10564                     
10565                     {
10566                         tag: 'label',
10567                         'for' :  id,
10568                         cls : 'control-label col-sm-' + this.labelWidth,
10569                         html : this.fieldLabel
10570                         
10571                     },
10572                     {
10573                         cls : "col-sm-" + (12 - this.labelWidth), 
10574                         cn: [
10575                             combobox
10576                         ]
10577                     }
10578                     
10579                 ];
10580         } else if ( this.fieldLabel.length) {
10581                 Roo.log(" label");
10582                  cfg.cn = [
10583                    
10584                     {
10585                         tag: 'label',
10586                         //cls : 'input-group-addon',
10587                         html : this.fieldLabel
10588                         
10589                     },
10590                     
10591                     combobox
10592                     
10593                 ];
10594
10595         } else {
10596             
10597                 Roo.log(" no label && no align");
10598                 cfg = combobox
10599                      
10600                 
10601         }
10602          
10603         var settings=this;
10604         ['xs','sm','md','lg'].map(function(size){
10605             if (settings[size]) {
10606                 cfg.cls += ' col-' + size + '-' + settings[size];
10607             }
10608         });
10609         
10610         return cfg;
10611         
10612     },
10613     
10614     // private
10615     initEvents: function()
10616     {
10617         
10618         if (!this.store) {
10619             throw "can not find store for combo";
10620         }
10621         this.store = Roo.factory(this.store, Roo.data);
10622         
10623         if(this.tickable){
10624             this.initTickableEvents();
10625             return;
10626         }
10627         
10628         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10629         
10630         if(this.hiddenName){
10631             
10632             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10633             
10634             this.hiddenField.dom.value =
10635                 this.hiddenValue !== undefined ? this.hiddenValue :
10636                 this.value !== undefined ? this.value : '';
10637
10638             // prevent input submission
10639             this.el.dom.removeAttribute('name');
10640             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10641              
10642              
10643         }
10644         //if(Roo.isGecko){
10645         //    this.el.dom.setAttribute('autocomplete', 'off');
10646         //}
10647         
10648         var cls = 'x-combo-list';
10649         
10650         //this.list = new Roo.Layer({
10651         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10652         //});
10653         
10654         var _this = this;
10655         
10656         (function(){
10657             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10658             _this.list.setWidth(lw);
10659         }).defer(100);
10660         
10661         this.list.on('mouseover', this.onViewOver, this);
10662         this.list.on('mousemove', this.onViewMove, this);
10663         
10664         this.list.on('scroll', this.onViewScroll, this);
10665         
10666         /*
10667         this.list.swallowEvent('mousewheel');
10668         this.assetHeight = 0;
10669
10670         if(this.title){
10671             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10672             this.assetHeight += this.header.getHeight();
10673         }
10674
10675         this.innerList = this.list.createChild({cls:cls+'-inner'});
10676         this.innerList.on('mouseover', this.onViewOver, this);
10677         this.innerList.on('mousemove', this.onViewMove, this);
10678         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10679         
10680         if(this.allowBlank && !this.pageSize && !this.disableClear){
10681             this.footer = this.list.createChild({cls:cls+'-ft'});
10682             this.pageTb = new Roo.Toolbar(this.footer);
10683            
10684         }
10685         if(this.pageSize){
10686             this.footer = this.list.createChild({cls:cls+'-ft'});
10687             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10688                     {pageSize: this.pageSize});
10689             
10690         }
10691         
10692         if (this.pageTb && this.allowBlank && !this.disableClear) {
10693             var _this = this;
10694             this.pageTb.add(new Roo.Toolbar.Fill(), {
10695                 cls: 'x-btn-icon x-btn-clear',
10696                 text: '&#160;',
10697                 handler: function()
10698                 {
10699                     _this.collapse();
10700                     _this.clearValue();
10701                     _this.onSelect(false, -1);
10702                 }
10703             });
10704         }
10705         if (this.footer) {
10706             this.assetHeight += this.footer.getHeight();
10707         }
10708         */
10709             
10710         if(!this.tpl){
10711             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10712         }
10713
10714         this.view = new Roo.View(this.list, this.tpl, {
10715             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10716         });
10717         //this.view.wrapEl.setDisplayed(false);
10718         this.view.on('click', this.onViewClick, this);
10719         
10720         
10721         
10722         this.store.on('beforeload', this.onBeforeLoad, this);
10723         this.store.on('load', this.onLoad, this);
10724         this.store.on('loadexception', this.onLoadException, this);
10725         /*
10726         if(this.resizable){
10727             this.resizer = new Roo.Resizable(this.list,  {
10728                pinned:true, handles:'se'
10729             });
10730             this.resizer.on('resize', function(r, w, h){
10731                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10732                 this.listWidth = w;
10733                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10734                 this.restrictHeight();
10735             }, this);
10736             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10737         }
10738         */
10739         if(!this.editable){
10740             this.editable = true;
10741             this.setEditable(false);
10742         }
10743         
10744         /*
10745         
10746         if (typeof(this.events.add.listeners) != 'undefined') {
10747             
10748             this.addicon = this.wrap.createChild(
10749                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10750        
10751             this.addicon.on('click', function(e) {
10752                 this.fireEvent('add', this);
10753             }, this);
10754         }
10755         if (typeof(this.events.edit.listeners) != 'undefined') {
10756             
10757             this.editicon = this.wrap.createChild(
10758                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10759             if (this.addicon) {
10760                 this.editicon.setStyle('margin-left', '40px');
10761             }
10762             this.editicon.on('click', function(e) {
10763                 
10764                 // we fire even  if inothing is selected..
10765                 this.fireEvent('edit', this, this.lastData );
10766                 
10767             }, this);
10768         }
10769         */
10770         
10771         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10772             "up" : function(e){
10773                 this.inKeyMode = true;
10774                 this.selectPrev();
10775             },
10776
10777             "down" : function(e){
10778                 if(!this.isExpanded()){
10779                     this.onTriggerClick();
10780                 }else{
10781                     this.inKeyMode = true;
10782                     this.selectNext();
10783                 }
10784             },
10785
10786             "enter" : function(e){
10787 //                this.onViewClick();
10788                 //return true;
10789                 this.collapse();
10790                 
10791                 if(this.fireEvent("specialkey", this, e)){
10792                     this.onViewClick(false);
10793                 }
10794                 
10795                 return true;
10796             },
10797
10798             "esc" : function(e){
10799                 this.collapse();
10800             },
10801
10802             "tab" : function(e){
10803                 this.collapse();
10804                 
10805                 if(this.fireEvent("specialkey", this, e)){
10806                     this.onViewClick(false);
10807                 }
10808                 
10809                 return true;
10810             },
10811
10812             scope : this,
10813
10814             doRelay : function(foo, bar, hname){
10815                 if(hname == 'down' || this.scope.isExpanded()){
10816                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10817                 }
10818                 return true;
10819             },
10820
10821             forceKeyDown: true
10822         });
10823         
10824         
10825         this.queryDelay = Math.max(this.queryDelay || 10,
10826                 this.mode == 'local' ? 10 : 250);
10827         
10828         
10829         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10830         
10831         if(this.typeAhead){
10832             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10833         }
10834         if(this.editable !== false){
10835             this.inputEl().on("keyup", this.onKeyUp, this);
10836         }
10837         if(this.forceSelection){
10838             this.inputEl().on('blur', this.doForce, this);
10839         }
10840         
10841         if(this.multiple){
10842             this.choices = this.el.select('ul.select2-choices', true).first();
10843             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10844         }
10845     },
10846     
10847     initTickableEvents: function()
10848     {   
10849         this.createList();
10850         
10851         if(this.hiddenName){
10852             
10853             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10854             
10855             this.hiddenField.dom.value =
10856                 this.hiddenValue !== undefined ? this.hiddenValue :
10857                 this.value !== undefined ? this.value : '';
10858
10859             // prevent input submission
10860             this.el.dom.removeAttribute('name');
10861             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10862              
10863              
10864         }
10865         
10866 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10867         
10868         this.choices = this.el.select('ul.select2-choices', true).first();
10869         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10870         if(this.triggerList){
10871             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10872         }
10873          
10874         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10875         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10876         
10877         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10878         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10879         
10880         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10881         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10882         
10883         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10884         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10885         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10886         
10887         this.okBtn.hide();
10888         this.cancelBtn.hide();
10889         
10890         var _this = this;
10891         
10892         (function(){
10893             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10894             _this.list.setWidth(lw);
10895         }).defer(100);
10896         
10897         this.list.on('mouseover', this.onViewOver, this);
10898         this.list.on('mousemove', this.onViewMove, this);
10899         
10900         this.list.on('scroll', this.onViewScroll, this);
10901         
10902         if(!this.tpl){
10903             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>';
10904         }
10905
10906         this.view = new Roo.View(this.list, this.tpl, {
10907             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10908         });
10909         
10910         //this.view.wrapEl.setDisplayed(false);
10911         this.view.on('click', this.onViewClick, this);
10912         
10913         
10914         
10915         this.store.on('beforeload', this.onBeforeLoad, this);
10916         this.store.on('load', this.onLoad, this);
10917         this.store.on('loadexception', this.onLoadException, this);
10918         
10919 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10920 //            "up" : function(e){
10921 //                this.inKeyMode = true;
10922 //                this.selectPrev();
10923 //            },
10924 //
10925 //            "down" : function(e){
10926 //                if(!this.isExpanded()){
10927 //                    this.onTriggerClick();
10928 //                }else{
10929 //                    this.inKeyMode = true;
10930 //                    this.selectNext();
10931 //                }
10932 //            },
10933 //
10934 //            "enter" : function(e){
10935 ////                this.onViewClick();
10936 //                //return true;
10937 //                this.collapse();
10938 //                
10939 //                if(this.fireEvent("specialkey", this, e)){
10940 //                    this.onViewClick(false);
10941 //                }
10942 //                
10943 //                return true;
10944 //            },
10945 //
10946 //            "esc" : function(e){
10947 //                this.collapse();
10948 //            },
10949 //
10950 //            "tab" : function(e){
10951 //                this.collapse();
10952 //                
10953 //                if(this.fireEvent("specialkey", this, e)){
10954 //                    this.onViewClick(false);
10955 //                }
10956 //                
10957 //                return true;
10958 //            },
10959 //
10960 //            scope : this,
10961 //
10962 //            doRelay : function(foo, bar, hname){
10963 //                if(hname == 'down' || this.scope.isExpanded()){
10964 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10965 //                }
10966 //                return true;
10967 //            },
10968 //
10969 //            forceKeyDown: true
10970 //        });
10971         
10972         
10973         this.queryDelay = Math.max(this.queryDelay || 10,
10974                 this.mode == 'local' ? 10 : 250);
10975         
10976         
10977         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10978         
10979         if(this.typeAhead){
10980             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10981         }
10982     },
10983
10984     onDestroy : function(){
10985         if(this.view){
10986             this.view.setStore(null);
10987             this.view.el.removeAllListeners();
10988             this.view.el.remove();
10989             this.view.purgeListeners();
10990         }
10991         if(this.list){
10992             this.list.dom.innerHTML  = '';
10993         }
10994         
10995         if(this.store){
10996             this.store.un('beforeload', this.onBeforeLoad, this);
10997             this.store.un('load', this.onLoad, this);
10998             this.store.un('loadexception', this.onLoadException, this);
10999         }
11000         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11001     },
11002
11003     // private
11004     fireKey : function(e){
11005         if(e.isNavKeyPress() && !this.list.isVisible()){
11006             this.fireEvent("specialkey", this, e);
11007         }
11008     },
11009
11010     // private
11011     onResize: function(w, h){
11012 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11013 //        
11014 //        if(typeof w != 'number'){
11015 //            // we do not handle it!?!?
11016 //            return;
11017 //        }
11018 //        var tw = this.trigger.getWidth();
11019 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11020 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11021 //        var x = w - tw;
11022 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11023 //            
11024 //        //this.trigger.setStyle('left', x+'px');
11025 //        
11026 //        if(this.list && this.listWidth === undefined){
11027 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11028 //            this.list.setWidth(lw);
11029 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11030 //        }
11031         
11032     
11033         
11034     },
11035
11036     /**
11037      * Allow or prevent the user from directly editing the field text.  If false is passed,
11038      * the user will only be able to select from the items defined in the dropdown list.  This method
11039      * is the runtime equivalent of setting the 'editable' config option at config time.
11040      * @param {Boolean} value True to allow the user to directly edit the field text
11041      */
11042     setEditable : function(value){
11043         if(value == this.editable){
11044             return;
11045         }
11046         this.editable = value;
11047         if(!value){
11048             this.inputEl().dom.setAttribute('readOnly', true);
11049             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11050             this.inputEl().addClass('x-combo-noedit');
11051         }else{
11052             this.inputEl().dom.setAttribute('readOnly', false);
11053             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11054             this.inputEl().removeClass('x-combo-noedit');
11055         }
11056     },
11057
11058     // private
11059     
11060     onBeforeLoad : function(combo,opts){
11061         if(!this.hasFocus){
11062             return;
11063         }
11064          if (!opts.add) {
11065             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11066          }
11067 //        this.restrictHeight();
11068         this.selectedIndex = -1;
11069     },
11070
11071     // private
11072     onLoad : function(){
11073         
11074         this.hasQuery = false;
11075         
11076         if(!this.hasFocus){
11077             return;
11078         }
11079         
11080         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11081             this.loading.hide();
11082         }
11083         
11084         if(this.store.getCount() > 0){
11085             this.expand();
11086 //            this.restrictHeight();
11087             if(this.lastQuery == this.allQuery){
11088                 if(this.editable && !this.tickable){
11089                     this.inputEl().dom.select();
11090                 }
11091                 
11092                 if(
11093                     !this.selectByValue(this.value, true) &&
11094                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11095                     this.store.lastOptions.add != true)
11096                 ){
11097                     this.select(0, true);
11098                 }
11099             }else{
11100                 if(this.autoFocus){
11101                     this.selectNext();
11102                 }
11103                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11104                     this.taTask.delay(this.typeAheadDelay);
11105                 }
11106             }
11107         }else{
11108             this.onEmptyResults();
11109         }
11110         
11111         //this.el.focus();
11112     },
11113     // private
11114     onLoadException : function()
11115     {
11116         this.hasQuery = false;
11117         
11118         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11119             this.loading.hide();
11120         }
11121         
11122         this.collapse();
11123         Roo.log(this.store.reader.jsonData);
11124         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11125             // fixme
11126             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11127         }
11128         
11129         
11130     },
11131     // private
11132     onTypeAhead : function(){
11133         if(this.store.getCount() > 0){
11134             var r = this.store.getAt(0);
11135             var newValue = r.data[this.displayField];
11136             var len = newValue.length;
11137             var selStart = this.getRawValue().length;
11138             
11139             if(selStart != len){
11140                 this.setRawValue(newValue);
11141                 this.selectText(selStart, newValue.length);
11142             }
11143         }
11144     },
11145
11146     // private
11147     onSelect : function(record, index){
11148         
11149         if(this.fireEvent('beforeselect', this, record, index) !== false){
11150         
11151             this.setFromData(index > -1 ? record.data : false);
11152             
11153             this.collapse();
11154             this.fireEvent('select', this, record, index);
11155         }
11156     },
11157
11158     /**
11159      * Returns the currently selected field value or empty string if no value is set.
11160      * @return {String} value The selected value
11161      */
11162     getValue : function(){
11163         
11164         if(this.multiple){
11165             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11166         }
11167         
11168         if(this.valueField){
11169             return typeof this.value != 'undefined' ? this.value : '';
11170         }else{
11171             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11172         }
11173     },
11174
11175     /**
11176      * Clears any text/value currently set in the field
11177      */
11178     clearValue : function(){
11179         if(this.hiddenField){
11180             this.hiddenField.dom.value = '';
11181         }
11182         this.value = '';
11183         this.setRawValue('');
11184         this.lastSelectionText = '';
11185         
11186     },
11187
11188     /**
11189      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11190      * will be displayed in the field.  If the value does not match the data value of an existing item,
11191      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11192      * Otherwise the field will be blank (although the value will still be set).
11193      * @param {String} value The value to match
11194      */
11195     setValue : function(v){
11196         if(this.multiple){
11197             this.syncValue();
11198             return;
11199         }
11200         
11201         var text = v;
11202         if(this.valueField){
11203             var r = this.findRecord(this.valueField, v);
11204             if(r){
11205                 text = r.data[this.displayField];
11206             }else if(this.valueNotFoundText !== undefined){
11207                 text = this.valueNotFoundText;
11208             }
11209         }
11210         this.lastSelectionText = text;
11211         if(this.hiddenField){
11212             this.hiddenField.dom.value = v;
11213         }
11214         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11215         this.value = v;
11216     },
11217     /**
11218      * @property {Object} the last set data for the element
11219      */
11220     
11221     lastData : false,
11222     /**
11223      * Sets the value of the field based on a object which is related to the record format for the store.
11224      * @param {Object} value the value to set as. or false on reset?
11225      */
11226     setFromData : function(o){
11227         
11228         if(this.multiple){
11229             if(typeof o.display_name !== 'string'){
11230                 for(var i=0;i<o.display_name.length;i++){
11231                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11232                 }
11233                 return;
11234             }
11235             this.addItem(o);
11236             return;
11237         }
11238             
11239         var dv = ''; // display value
11240         var vv = ''; // value value..
11241         this.lastData = o;
11242         if (this.displayField) {
11243             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11244         } else {
11245             // this is an error condition!!!
11246             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11247         }
11248         
11249         if(this.valueField){
11250             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11251         }
11252         
11253         if(this.hiddenField){
11254             this.hiddenField.dom.value = vv;
11255             
11256             this.lastSelectionText = dv;
11257             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11258             this.value = vv;
11259             return;
11260         }
11261         // no hidden field.. - we store the value in 'value', but still display
11262         // display field!!!!
11263         this.lastSelectionText = dv;
11264         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11265         this.value = vv;
11266         
11267         
11268     },
11269     // private
11270     reset : function(){
11271         // overridden so that last data is reset..
11272         this.setValue(this.originalValue);
11273         this.clearInvalid();
11274         this.lastData = false;
11275         if (this.view) {
11276             this.view.clearSelections();
11277         }
11278     },
11279     // private
11280     findRecord : function(prop, value){
11281         var record;
11282         if(this.store.getCount() > 0){
11283             this.store.each(function(r){
11284                 if(r.data[prop] == value){
11285                     record = r;
11286                     return false;
11287                 }
11288                 return true;
11289             });
11290         }
11291         return record;
11292     },
11293     
11294     getName: function()
11295     {
11296         // returns hidden if it's set..
11297         if (!this.rendered) {return ''};
11298         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11299         
11300     },
11301     // private
11302     onViewMove : function(e, t){
11303         this.inKeyMode = false;
11304     },
11305
11306     // private
11307     onViewOver : function(e, t){
11308         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11309             return;
11310         }
11311         var item = this.view.findItemFromChild(t);
11312         
11313         if(item){
11314             var index = this.view.indexOf(item);
11315             this.select(index, false);
11316         }
11317     },
11318
11319     // private
11320     onViewClick : function(view, doFocus, el, e)
11321     {
11322         var index = this.view.getSelectedIndexes()[0];
11323         
11324         var r = this.store.getAt(index);
11325         
11326         if(this.tickable){
11327             
11328             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11329                 return;
11330             }
11331             
11332             var rm = false;
11333             var _this = this;
11334             
11335             Roo.each(this.tickItems, function(v,k){
11336                 
11337                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11338                     _this.tickItems.splice(k, 1);
11339                     rm = true;
11340                     return;
11341                 }
11342             })
11343             
11344             if(rm){
11345                 return;
11346             }
11347             
11348             this.tickItems.push(r.data);
11349             return;
11350         }
11351         
11352         if(r){
11353             this.onSelect(r, index);
11354         }
11355         if(doFocus !== false && !this.blockFocus){
11356             this.inputEl().focus();
11357         }
11358     },
11359
11360     // private
11361     restrictHeight : function(){
11362         //this.innerList.dom.style.height = '';
11363         //var inner = this.innerList.dom;
11364         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11365         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11366         //this.list.beginUpdate();
11367         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11368         this.list.alignTo(this.inputEl(), this.listAlign);
11369         this.list.alignTo(this.inputEl(), this.listAlign);
11370         //this.list.endUpdate();
11371     },
11372
11373     // private
11374     onEmptyResults : function(){
11375         this.collapse();
11376     },
11377
11378     /**
11379      * Returns true if the dropdown list is expanded, else false.
11380      */
11381     isExpanded : function(){
11382         return this.list.isVisible();
11383     },
11384
11385     /**
11386      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11387      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11388      * @param {String} value The data value of the item to select
11389      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11390      * selected item if it is not currently in view (defaults to true)
11391      * @return {Boolean} True if the value matched an item in the list, else false
11392      */
11393     selectByValue : function(v, scrollIntoView){
11394         if(v !== undefined && v !== null){
11395             var r = this.findRecord(this.valueField || this.displayField, v);
11396             if(r){
11397                 this.select(this.store.indexOf(r), scrollIntoView);
11398                 return true;
11399             }
11400         }
11401         return false;
11402     },
11403
11404     /**
11405      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11406      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11407      * @param {Number} index The zero-based index of the list item to select
11408      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11409      * selected item if it is not currently in view (defaults to true)
11410      */
11411     select : function(index, scrollIntoView){
11412         this.selectedIndex = index;
11413         this.view.select(index);
11414         if(scrollIntoView !== false){
11415             var el = this.view.getNode(index);
11416             if(el && !this.multiple && !this.tickable){
11417                 this.list.scrollChildIntoView(el, false);
11418             }
11419         }
11420     },
11421
11422     // private
11423     selectNext : function(){
11424         var ct = this.store.getCount();
11425         if(ct > 0){
11426             if(this.selectedIndex == -1){
11427                 this.select(0);
11428             }else if(this.selectedIndex < ct-1){
11429                 this.select(this.selectedIndex+1);
11430             }
11431         }
11432     },
11433
11434     // private
11435     selectPrev : function(){
11436         var ct = this.store.getCount();
11437         if(ct > 0){
11438             if(this.selectedIndex == -1){
11439                 this.select(0);
11440             }else if(this.selectedIndex != 0){
11441                 this.select(this.selectedIndex-1);
11442             }
11443         }
11444     },
11445
11446     // private
11447     onKeyUp : function(e){
11448         if(this.editable !== false && !e.isSpecialKey()){
11449             this.lastKey = e.getKey();
11450             this.dqTask.delay(this.queryDelay);
11451         }
11452     },
11453
11454     // private
11455     validateBlur : function(){
11456         return !this.list || !this.list.isVisible();   
11457     },
11458
11459     // private
11460     initQuery : function(){
11461         this.doQuery(this.getRawValue());
11462     },
11463
11464     // private
11465     doForce : function(){
11466         if(this.inputEl().dom.value.length > 0){
11467             this.inputEl().dom.value =
11468                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11469              
11470         }
11471     },
11472
11473     /**
11474      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11475      * query allowing the query action to be canceled if needed.
11476      * @param {String} query The SQL query to execute
11477      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11478      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11479      * saved in the current store (defaults to false)
11480      */
11481     doQuery : function(q, forceAll){
11482         
11483         if(q === undefined || q === null){
11484             q = '';
11485         }
11486         var qe = {
11487             query: q,
11488             forceAll: forceAll,
11489             combo: this,
11490             cancel:false
11491         };
11492         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11493             return false;
11494         }
11495         q = qe.query;
11496         
11497         forceAll = qe.forceAll;
11498         if(forceAll === true || (q.length >= this.minChars)){
11499             
11500             this.hasQuery = true;
11501             
11502             if(this.lastQuery != q || this.alwaysQuery){
11503                 this.lastQuery = q;
11504                 if(this.mode == 'local'){
11505                     this.selectedIndex = -1;
11506                     if(forceAll){
11507                         this.store.clearFilter();
11508                     }else{
11509                         this.store.filter(this.displayField, q);
11510                     }
11511                     this.onLoad();
11512                 }else{
11513                     this.store.baseParams[this.queryParam] = q;
11514                     
11515                     var options = {params : this.getParams(q)};
11516                     
11517                     if(this.loadNext){
11518                         options.add = true;
11519                         options.params.start = this.page * this.pageSize;
11520                     }
11521                     
11522                     this.store.load(options);
11523                     /*
11524                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11525                      *  we should expand the list on onLoad
11526                      *  so command out it
11527                      */
11528 //                    this.expand();
11529                 }
11530             }else{
11531                 this.selectedIndex = -1;
11532                 this.onLoad();   
11533             }
11534         }
11535         
11536         this.loadNext = false;
11537     },
11538
11539     // private
11540     getParams : function(q){
11541         var p = {};
11542         //p[this.queryParam] = q;
11543         
11544         if(this.pageSize){
11545             p.start = 0;
11546             p.limit = this.pageSize;
11547         }
11548         return p;
11549     },
11550
11551     /**
11552      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11553      */
11554     collapse : function(){
11555         if(!this.isExpanded()){
11556             return;
11557         }
11558         
11559         this.list.hide();
11560         
11561         if(this.tickable){
11562             this.okBtn.hide();
11563             this.cancelBtn.hide();
11564             this.trigger.show();
11565         }
11566         
11567         Roo.get(document).un('mousedown', this.collapseIf, this);
11568         Roo.get(document).un('mousewheel', this.collapseIf, this);
11569         if (!this.editable) {
11570             Roo.get(document).un('keydown', this.listKeyPress, this);
11571         }
11572         this.fireEvent('collapse', this);
11573     },
11574
11575     // private
11576     collapseIf : function(e){
11577         var in_combo  = e.within(this.el);
11578         var in_list =  e.within(this.list);
11579         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11580         
11581         if (in_combo || in_list || is_list) {
11582             //e.stopPropagation();
11583             return;
11584         }
11585         
11586         if(this.tickable){
11587             this.onTickableFooterButtonClick(e, false, false);
11588         }
11589
11590         this.collapse();
11591         
11592     },
11593
11594     /**
11595      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11596      */
11597     expand : function(){
11598        
11599         if(this.isExpanded() || !this.hasFocus){
11600             return;
11601         }
11602         
11603         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11604         this.list.setWidth(lw);
11605         
11606         
11607          Roo.log('expand');
11608         
11609         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11610         this.list.setWidth(lw);
11611             
11612         this.list.show();
11613         
11614         this.restrictHeight();
11615         
11616         if(this.tickable){
11617             
11618             this.tickItems = Roo.apply([], this.item);
11619             
11620             this.okBtn.show();
11621             this.cancelBtn.show();
11622             this.trigger.hide();
11623             
11624         }
11625         
11626         Roo.get(document).on('mousedown', this.collapseIf, this);
11627         Roo.get(document).on('mousewheel', this.collapseIf, this);
11628         if (!this.editable) {
11629             Roo.get(document).on('keydown', this.listKeyPress, this);
11630         }
11631         
11632         this.fireEvent('expand', this);
11633     },
11634
11635     // private
11636     // Implements the default empty TriggerField.onTriggerClick function
11637     onTriggerClick : function(e)
11638     {
11639         Roo.log('trigger click');
11640         
11641         if(this.disabled || !this.triggerList){
11642             return;
11643         }
11644         
11645         this.page = 0;
11646         this.loadNext = false;
11647         
11648         if(this.isExpanded()){
11649             this.collapse();
11650             if (!this.blockFocus) {
11651                 this.inputEl().focus();
11652             }
11653             
11654         }else {
11655             this.hasFocus = true;
11656             if(this.triggerAction == 'all') {
11657                 this.doQuery(this.allQuery, true);
11658             } else {
11659                 this.doQuery(this.getRawValue());
11660             }
11661             if (!this.blockFocus) {
11662                 this.inputEl().focus();
11663             }
11664         }
11665     },
11666     
11667     onTickableTriggerClick : function(e)
11668     {
11669         if(this.disabled){
11670             return;
11671         }
11672         
11673         this.page = 0;
11674         this.loadNext = false;
11675         this.hasFocus = true;
11676         
11677         if(this.triggerAction == 'all') {
11678             this.doQuery(this.allQuery, true);
11679         } else {
11680             this.doQuery(this.getRawValue());
11681         }
11682     },
11683     
11684     onSearchFieldClick : function(e)
11685     {
11686         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11687             return;
11688         }
11689         
11690         this.page = 0;
11691         this.loadNext = false;
11692         this.hasFocus = true;
11693         
11694         if(this.triggerAction == 'all') {
11695             this.doQuery(this.allQuery, true);
11696         } else {
11697             this.doQuery(this.getRawValue());
11698         }
11699     },
11700     
11701     listKeyPress : function(e)
11702     {
11703         //Roo.log('listkeypress');
11704         // scroll to first matching element based on key pres..
11705         if (e.isSpecialKey()) {
11706             return false;
11707         }
11708         var k = String.fromCharCode(e.getKey()).toUpperCase();
11709         //Roo.log(k);
11710         var match  = false;
11711         var csel = this.view.getSelectedNodes();
11712         var cselitem = false;
11713         if (csel.length) {
11714             var ix = this.view.indexOf(csel[0]);
11715             cselitem  = this.store.getAt(ix);
11716             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11717                 cselitem = false;
11718             }
11719             
11720         }
11721         
11722         this.store.each(function(v) { 
11723             if (cselitem) {
11724                 // start at existing selection.
11725                 if (cselitem.id == v.id) {
11726                     cselitem = false;
11727                 }
11728                 return true;
11729             }
11730                 
11731             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11732                 match = this.store.indexOf(v);
11733                 return false;
11734             }
11735             return true;
11736         }, this);
11737         
11738         if (match === false) {
11739             return true; // no more action?
11740         }
11741         // scroll to?
11742         this.view.select(match);
11743         var sn = Roo.get(this.view.getSelectedNodes()[0])
11744         //sn.scrollIntoView(sn.dom.parentNode, false);
11745     },
11746     
11747     onViewScroll : function(e, t){
11748         
11749         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11750             return;
11751         }
11752         
11753         this.hasQuery = true;
11754         
11755         this.loading = this.list.select('.loading', true).first();
11756         
11757         if(this.loading === null){
11758             this.list.createChild({
11759                 tag: 'div',
11760                 cls: 'loading select2-more-results select2-active',
11761                 html: 'Loading more results...'
11762             })
11763             
11764             this.loading = this.list.select('.loading', true).first();
11765             
11766             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11767             
11768             this.loading.hide();
11769         }
11770         
11771         this.loading.show();
11772         
11773         var _combo = this;
11774         
11775         this.page++;
11776         this.loadNext = true;
11777         
11778         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11779         
11780         return;
11781     },
11782     
11783     addItem : function(o)
11784     {   
11785         var dv = ''; // display value
11786         
11787         if (this.displayField) {
11788             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11789         } else {
11790             // this is an error condition!!!
11791             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11792         }
11793         
11794         if(!dv.length){
11795             return;
11796         }
11797         
11798         var choice = this.choices.createChild({
11799             tag: 'li',
11800             cls: 'select2-search-choice',
11801             cn: [
11802                 {
11803                     tag: 'div',
11804                     html: dv
11805                 },
11806                 {
11807                     tag: 'a',
11808                     href: '#',
11809                     cls: 'select2-search-choice-close',
11810                     tabindex: '-1'
11811                 }
11812             ]
11813             
11814         }, this.searchField);
11815         
11816         var close = choice.select('a.select2-search-choice-close', true).first()
11817         
11818         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11819         
11820         this.item.push(o);
11821         
11822         this.lastData = o;
11823         
11824         this.syncValue();
11825         
11826         this.inputEl().dom.value = '';
11827         
11828     },
11829     
11830     onRemoveItem : function(e, _self, o)
11831     {
11832         e.preventDefault();
11833         var index = this.item.indexOf(o.data) * 1;
11834         
11835         if( index < 0){
11836             Roo.log('not this item?!');
11837             return;
11838         }
11839         
11840         this.item.splice(index, 1);
11841         o.item.remove();
11842         
11843         this.syncValue();
11844         
11845         this.fireEvent('remove', this, e);
11846         
11847     },
11848     
11849     syncValue : function()
11850     {
11851         if(!this.item.length){
11852             this.clearValue();
11853             return;
11854         }
11855             
11856         var value = [];
11857         var _this = this;
11858         Roo.each(this.item, function(i){
11859             if(_this.valueField){
11860                 value.push(i[_this.valueField]);
11861                 return;
11862             }
11863
11864             value.push(i);
11865         });
11866
11867         this.value = value.join(',');
11868
11869         if(this.hiddenField){
11870             this.hiddenField.dom.value = this.value;
11871         }
11872     },
11873     
11874     clearItem : function()
11875     {
11876         if(!this.multiple){
11877             return;
11878         }
11879         
11880         this.item = [];
11881         
11882         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11883            c.remove();
11884         });
11885         
11886         this.syncValue();
11887     },
11888     
11889     inputEl: function ()
11890     {
11891         if(this.tickable){
11892             return this.searchField;
11893         }
11894         return this.el.select('input.form-control',true).first();
11895     },
11896     
11897     
11898     onTickableFooterButtonClick : function(e, btn, el)
11899     {
11900         e.preventDefault();
11901         
11902         if(btn && btn.name == 'cancel'){
11903             this.tickItems = Roo.apply([], this.item);
11904             this.collapse();
11905             return;
11906         }
11907         
11908         this.clearItem();
11909         
11910         var _this = this;
11911         
11912         Roo.each(this.tickItems, function(o){
11913             _this.addItem(o);
11914         });
11915         
11916         this.collapse();
11917         
11918     }
11919     
11920     
11921
11922     /** 
11923     * @cfg {Boolean} grow 
11924     * @hide 
11925     */
11926     /** 
11927     * @cfg {Number} growMin 
11928     * @hide 
11929     */
11930     /** 
11931     * @cfg {Number} growMax 
11932     * @hide 
11933     */
11934     /**
11935      * @hide
11936      * @method autoSize
11937      */
11938 });
11939 /*
11940  * Based on:
11941  * Ext JS Library 1.1.1
11942  * Copyright(c) 2006-2007, Ext JS, LLC.
11943  *
11944  * Originally Released Under LGPL - original licence link has changed is not relivant.
11945  *
11946  * Fork - LGPL
11947  * <script type="text/javascript">
11948  */
11949
11950 /**
11951  * @class Roo.View
11952  * @extends Roo.util.Observable
11953  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11954  * This class also supports single and multi selection modes. <br>
11955  * Create a data model bound view:
11956  <pre><code>
11957  var store = new Roo.data.Store(...);
11958
11959  var view = new Roo.View({
11960     el : "my-element",
11961     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11962  
11963     singleSelect: true,
11964     selectedClass: "ydataview-selected",
11965     store: store
11966  });
11967
11968  // listen for node click?
11969  view.on("click", function(vw, index, node, e){
11970  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11971  });
11972
11973  // load XML data
11974  dataModel.load("foobar.xml");
11975  </code></pre>
11976  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11977  * <br><br>
11978  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11979  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11980  * 
11981  * Note: old style constructor is still suported (container, template, config)
11982  * 
11983  * @constructor
11984  * Create a new View
11985  * @param {Object} config The config object
11986  * 
11987  */
11988 Roo.View = function(config, depreciated_tpl, depreciated_config){
11989     
11990     this.parent = false;
11991     
11992     if (typeof(depreciated_tpl) == 'undefined') {
11993         // new way.. - universal constructor.
11994         Roo.apply(this, config);
11995         this.el  = Roo.get(this.el);
11996     } else {
11997         // old format..
11998         this.el  = Roo.get(config);
11999         this.tpl = depreciated_tpl;
12000         Roo.apply(this, depreciated_config);
12001     }
12002     this.wrapEl  = this.el.wrap().wrap();
12003     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12004     
12005     
12006     if(typeof(this.tpl) == "string"){
12007         this.tpl = new Roo.Template(this.tpl);
12008     } else {
12009         // support xtype ctors..
12010         this.tpl = new Roo.factory(this.tpl, Roo);
12011     }
12012     
12013     
12014     this.tpl.compile();
12015     
12016     /** @private */
12017     this.addEvents({
12018         /**
12019          * @event beforeclick
12020          * Fires before a click is processed. Returns false to cancel the default action.
12021          * @param {Roo.View} this
12022          * @param {Number} index The index of the target node
12023          * @param {HTMLElement} node The target node
12024          * @param {Roo.EventObject} e The raw event object
12025          */
12026             "beforeclick" : true,
12027         /**
12028          * @event click
12029          * Fires when a template node is clicked.
12030          * @param {Roo.View} this
12031          * @param {Number} index The index of the target node
12032          * @param {HTMLElement} node The target node
12033          * @param {Roo.EventObject} e The raw event object
12034          */
12035             "click" : true,
12036         /**
12037          * @event dblclick
12038          * Fires when a template node is double clicked.
12039          * @param {Roo.View} this
12040          * @param {Number} index The index of the target node
12041          * @param {HTMLElement} node The target node
12042          * @param {Roo.EventObject} e The raw event object
12043          */
12044             "dblclick" : true,
12045         /**
12046          * @event contextmenu
12047          * Fires when a template node is right clicked.
12048          * @param {Roo.View} this
12049          * @param {Number} index The index of the target node
12050          * @param {HTMLElement} node The target node
12051          * @param {Roo.EventObject} e The raw event object
12052          */
12053             "contextmenu" : true,
12054         /**
12055          * @event selectionchange
12056          * Fires when the selected nodes change.
12057          * @param {Roo.View} this
12058          * @param {Array} selections Array of the selected nodes
12059          */
12060             "selectionchange" : true,
12061     
12062         /**
12063          * @event beforeselect
12064          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12065          * @param {Roo.View} this
12066          * @param {HTMLElement} node The node to be selected
12067          * @param {Array} selections Array of currently selected nodes
12068          */
12069             "beforeselect" : true,
12070         /**
12071          * @event preparedata
12072          * Fires on every row to render, to allow you to change the data.
12073          * @param {Roo.View} this
12074          * @param {Object} data to be rendered (change this)
12075          */
12076           "preparedata" : true
12077           
12078           
12079         });
12080
12081
12082
12083     this.el.on({
12084         "click": this.onClick,
12085         "dblclick": this.onDblClick,
12086         "contextmenu": this.onContextMenu,
12087         scope:this
12088     });
12089
12090     this.selections = [];
12091     this.nodes = [];
12092     this.cmp = new Roo.CompositeElementLite([]);
12093     if(this.store){
12094         this.store = Roo.factory(this.store, Roo.data);
12095         this.setStore(this.store, true);
12096     }
12097     
12098     if ( this.footer && this.footer.xtype) {
12099            
12100          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12101         
12102         this.footer.dataSource = this.store
12103         this.footer.container = fctr;
12104         this.footer = Roo.factory(this.footer, Roo);
12105         fctr.insertFirst(this.el);
12106         
12107         // this is a bit insane - as the paging toolbar seems to detach the el..
12108 //        dom.parentNode.parentNode.parentNode
12109          // they get detached?
12110     }
12111     
12112     
12113     Roo.View.superclass.constructor.call(this);
12114     
12115     
12116 };
12117
12118 Roo.extend(Roo.View, Roo.util.Observable, {
12119     
12120      /**
12121      * @cfg {Roo.data.Store} store Data store to load data from.
12122      */
12123     store : false,
12124     
12125     /**
12126      * @cfg {String|Roo.Element} el The container element.
12127      */
12128     el : '',
12129     
12130     /**
12131      * @cfg {String|Roo.Template} tpl The template used by this View 
12132      */
12133     tpl : false,
12134     /**
12135      * @cfg {String} dataName the named area of the template to use as the data area
12136      *                          Works with domtemplates roo-name="name"
12137      */
12138     dataName: false,
12139     /**
12140      * @cfg {String} selectedClass The css class to add to selected nodes
12141      */
12142     selectedClass : "x-view-selected",
12143      /**
12144      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12145      */
12146     emptyText : "",
12147     
12148     /**
12149      * @cfg {String} text to display on mask (default Loading)
12150      */
12151     mask : false,
12152     /**
12153      * @cfg {Boolean} multiSelect Allow multiple selection
12154      */
12155     multiSelect : false,
12156     /**
12157      * @cfg {Boolean} singleSelect Allow single selection
12158      */
12159     singleSelect:  false,
12160     
12161     /**
12162      * @cfg {Boolean} toggleSelect - selecting 
12163      */
12164     toggleSelect : false,
12165     
12166     /**
12167      * @cfg {Boolean} tickable - selecting 
12168      */
12169     tickable : false,
12170     
12171     /**
12172      * Returns the element this view is bound to.
12173      * @return {Roo.Element}
12174      */
12175     getEl : function(){
12176         return this.wrapEl;
12177     },
12178     
12179     
12180
12181     /**
12182      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12183      */
12184     refresh : function(){
12185         Roo.log('refresh');
12186         var t = this.tpl;
12187         
12188         // if we are using something like 'domtemplate', then
12189         // the what gets used is:
12190         // t.applySubtemplate(NAME, data, wrapping data..)
12191         // the outer template then get' applied with
12192         //     the store 'extra data'
12193         // and the body get's added to the
12194         //      roo-name="data" node?
12195         //      <span class='roo-tpl-{name}'></span> ?????
12196         
12197         
12198         
12199         this.clearSelections();
12200         this.el.update("");
12201         var html = [];
12202         var records = this.store.getRange();
12203         if(records.length < 1) {
12204             
12205             // is this valid??  = should it render a template??
12206             
12207             this.el.update(this.emptyText);
12208             return;
12209         }
12210         var el = this.el;
12211         if (this.dataName) {
12212             this.el.update(t.apply(this.store.meta)); //????
12213             el = this.el.child('.roo-tpl-' + this.dataName);
12214         }
12215         
12216         for(var i = 0, len = records.length; i < len; i++){
12217             var data = this.prepareData(records[i].data, i, records[i]);
12218             this.fireEvent("preparedata", this, data, i, records[i]);
12219             
12220             var d = Roo.apply({}, data);
12221             
12222             if(this.tickable){
12223                 Roo.apply(d, {'roo-id' : Roo.id()});
12224                 
12225                 var _this = this;
12226             
12227                 Roo.each(this.parent.item, function(item){
12228                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12229                         return;
12230                     }
12231                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12232                 });
12233             }
12234             
12235             html[html.length] = Roo.util.Format.trim(
12236                 this.dataName ?
12237                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12238                     t.apply(d)
12239             );
12240         }
12241         
12242         
12243         
12244         el.update(html.join(""));
12245         this.nodes = el.dom.childNodes;
12246         this.updateIndexes(0);
12247     },
12248     
12249
12250     /**
12251      * Function to override to reformat the data that is sent to
12252      * the template for each node.
12253      * DEPRICATED - use the preparedata event handler.
12254      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12255      * a JSON object for an UpdateManager bound view).
12256      */
12257     prepareData : function(data, index, record)
12258     {
12259         this.fireEvent("preparedata", this, data, index, record);
12260         return data;
12261     },
12262
12263     onUpdate : function(ds, record){
12264          Roo.log('on update');   
12265         this.clearSelections();
12266         var index = this.store.indexOf(record);
12267         var n = this.nodes[index];
12268         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12269         n.parentNode.removeChild(n);
12270         this.updateIndexes(index, index);
12271     },
12272
12273     
12274     
12275 // --------- FIXME     
12276     onAdd : function(ds, records, index)
12277     {
12278         Roo.log(['on Add', ds, records, index] );        
12279         this.clearSelections();
12280         if(this.nodes.length == 0){
12281             this.refresh();
12282             return;
12283         }
12284         var n = this.nodes[index];
12285         for(var i = 0, len = records.length; i < len; i++){
12286             var d = this.prepareData(records[i].data, i, records[i]);
12287             if(n){
12288                 this.tpl.insertBefore(n, d);
12289             }else{
12290                 
12291                 this.tpl.append(this.el, d);
12292             }
12293         }
12294         this.updateIndexes(index);
12295     },
12296
12297     onRemove : function(ds, record, index){
12298         Roo.log('onRemove');
12299         this.clearSelections();
12300         var el = this.dataName  ?
12301             this.el.child('.roo-tpl-' + this.dataName) :
12302             this.el; 
12303         
12304         el.dom.removeChild(this.nodes[index]);
12305         this.updateIndexes(index);
12306     },
12307
12308     /**
12309      * Refresh an individual node.
12310      * @param {Number} index
12311      */
12312     refreshNode : function(index){
12313         this.onUpdate(this.store, this.store.getAt(index));
12314     },
12315
12316     updateIndexes : function(startIndex, endIndex){
12317         var ns = this.nodes;
12318         startIndex = startIndex || 0;
12319         endIndex = endIndex || ns.length - 1;
12320         for(var i = startIndex; i <= endIndex; i++){
12321             ns[i].nodeIndex = i;
12322         }
12323     },
12324
12325     /**
12326      * Changes the data store this view uses and refresh the view.
12327      * @param {Store} store
12328      */
12329     setStore : function(store, initial){
12330         if(!initial && this.store){
12331             this.store.un("datachanged", this.refresh);
12332             this.store.un("add", this.onAdd);
12333             this.store.un("remove", this.onRemove);
12334             this.store.un("update", this.onUpdate);
12335             this.store.un("clear", this.refresh);
12336             this.store.un("beforeload", this.onBeforeLoad);
12337             this.store.un("load", this.onLoad);
12338             this.store.un("loadexception", this.onLoad);
12339         }
12340         if(store){
12341           
12342             store.on("datachanged", this.refresh, this);
12343             store.on("add", this.onAdd, this);
12344             store.on("remove", this.onRemove, this);
12345             store.on("update", this.onUpdate, this);
12346             store.on("clear", this.refresh, this);
12347             store.on("beforeload", this.onBeforeLoad, this);
12348             store.on("load", this.onLoad, this);
12349             store.on("loadexception", this.onLoad, this);
12350         }
12351         
12352         if(store){
12353             this.refresh();
12354         }
12355     },
12356     /**
12357      * onbeforeLoad - masks the loading area.
12358      *
12359      */
12360     onBeforeLoad : function(store,opts)
12361     {
12362          Roo.log('onBeforeLoad');   
12363         if (!opts.add) {
12364             this.el.update("");
12365         }
12366         this.el.mask(this.mask ? this.mask : "Loading" ); 
12367     },
12368     onLoad : function ()
12369     {
12370         this.el.unmask();
12371     },
12372     
12373
12374     /**
12375      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12376      * @param {HTMLElement} node
12377      * @return {HTMLElement} The template node
12378      */
12379     findItemFromChild : function(node){
12380         var el = this.dataName  ?
12381             this.el.child('.roo-tpl-' + this.dataName,true) :
12382             this.el.dom; 
12383         
12384         if(!node || node.parentNode == el){
12385                     return node;
12386             }
12387             var p = node.parentNode;
12388             while(p && p != el){
12389             if(p.parentNode == el){
12390                 return p;
12391             }
12392             p = p.parentNode;
12393         }
12394             return null;
12395     },
12396
12397     /** @ignore */
12398     onClick : function(e){
12399         var item = this.findItemFromChild(e.getTarget());
12400         if(item){
12401             var index = this.indexOf(item);
12402             if(this.onItemClick(item, index, e) !== false){
12403                 this.fireEvent("click", this, index, item, e);
12404             }
12405         }else{
12406             this.clearSelections();
12407         }
12408     },
12409
12410     /** @ignore */
12411     onContextMenu : function(e){
12412         var item = this.findItemFromChild(e.getTarget());
12413         if(item){
12414             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12415         }
12416     },
12417
12418     /** @ignore */
12419     onDblClick : function(e){
12420         var item = this.findItemFromChild(e.getTarget());
12421         if(item){
12422             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12423         }
12424     },
12425
12426     onItemClick : function(item, index, e)
12427     {
12428         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12429             return false;
12430         }
12431         if (this.toggleSelect) {
12432             var m = this.isSelected(item) ? 'unselect' : 'select';
12433             Roo.log(m);
12434             var _t = this;
12435             _t[m](item, true, false);
12436             return true;
12437         }
12438         if(this.multiSelect || this.singleSelect){
12439             if(this.multiSelect && e.shiftKey && this.lastSelection){
12440                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12441             }else{
12442                 this.select(item, this.multiSelect && e.ctrlKey);
12443                 this.lastSelection = item;
12444             }
12445             
12446             if(!this.tickable){
12447                 e.preventDefault();
12448             }
12449             
12450         }
12451         return true;
12452     },
12453
12454     /**
12455      * Get the number of selected nodes.
12456      * @return {Number}
12457      */
12458     getSelectionCount : function(){
12459         return this.selections.length;
12460     },
12461
12462     /**
12463      * Get the currently selected nodes.
12464      * @return {Array} An array of HTMLElements
12465      */
12466     getSelectedNodes : function(){
12467         return this.selections;
12468     },
12469
12470     /**
12471      * Get the indexes of the selected nodes.
12472      * @return {Array}
12473      */
12474     getSelectedIndexes : function(){
12475         var indexes = [], s = this.selections;
12476         for(var i = 0, len = s.length; i < len; i++){
12477             indexes.push(s[i].nodeIndex);
12478         }
12479         return indexes;
12480     },
12481
12482     /**
12483      * Clear all selections
12484      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12485      */
12486     clearSelections : function(suppressEvent){
12487         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12488             this.cmp.elements = this.selections;
12489             this.cmp.removeClass(this.selectedClass);
12490             this.selections = [];
12491             if(!suppressEvent){
12492                 this.fireEvent("selectionchange", this, this.selections);
12493             }
12494         }
12495     },
12496
12497     /**
12498      * Returns true if the passed node is selected
12499      * @param {HTMLElement/Number} node The node or node index
12500      * @return {Boolean}
12501      */
12502     isSelected : function(node){
12503         var s = this.selections;
12504         if(s.length < 1){
12505             return false;
12506         }
12507         node = this.getNode(node);
12508         return s.indexOf(node) !== -1;
12509     },
12510
12511     /**
12512      * Selects nodes.
12513      * @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
12514      * @param {Boolean} keepExisting (optional) true to keep existing selections
12515      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12516      */
12517     select : function(nodeInfo, keepExisting, suppressEvent){
12518         if(nodeInfo instanceof Array){
12519             if(!keepExisting){
12520                 this.clearSelections(true);
12521             }
12522             for(var i = 0, len = nodeInfo.length; i < len; i++){
12523                 this.select(nodeInfo[i], true, true);
12524             }
12525             return;
12526         } 
12527         var node = this.getNode(nodeInfo);
12528         if(!node || this.isSelected(node)){
12529             return; // already selected.
12530         }
12531         if(!keepExisting){
12532             this.clearSelections(true);
12533         }
12534         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12535             Roo.fly(node).addClass(this.selectedClass);
12536             this.selections.push(node);
12537             if(!suppressEvent){
12538                 this.fireEvent("selectionchange", this, this.selections);
12539             }
12540         }
12541         
12542         
12543     },
12544       /**
12545      * Unselects nodes.
12546      * @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
12547      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12548      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12549      */
12550     unselect : function(nodeInfo, keepExisting, suppressEvent)
12551     {
12552         if(nodeInfo instanceof Array){
12553             Roo.each(this.selections, function(s) {
12554                 this.unselect(s, nodeInfo);
12555             }, this);
12556             return;
12557         }
12558         var node = this.getNode(nodeInfo);
12559         if(!node || !this.isSelected(node)){
12560             Roo.log("not selected");
12561             return; // not selected.
12562         }
12563         // fireevent???
12564         var ns = [];
12565         Roo.each(this.selections, function(s) {
12566             if (s == node ) {
12567                 Roo.fly(node).removeClass(this.selectedClass);
12568
12569                 return;
12570             }
12571             ns.push(s);
12572         },this);
12573         
12574         this.selections= ns;
12575         this.fireEvent("selectionchange", this, this.selections);
12576     },
12577
12578     /**
12579      * Gets a template node.
12580      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12581      * @return {HTMLElement} The node or null if it wasn't found
12582      */
12583     getNode : function(nodeInfo){
12584         if(typeof nodeInfo == "string"){
12585             return document.getElementById(nodeInfo);
12586         }else if(typeof nodeInfo == "number"){
12587             return this.nodes[nodeInfo];
12588         }
12589         return nodeInfo;
12590     },
12591
12592     /**
12593      * Gets a range template nodes.
12594      * @param {Number} startIndex
12595      * @param {Number} endIndex
12596      * @return {Array} An array of nodes
12597      */
12598     getNodes : function(start, end){
12599         var ns = this.nodes;
12600         start = start || 0;
12601         end = typeof end == "undefined" ? ns.length - 1 : end;
12602         var nodes = [];
12603         if(start <= end){
12604             for(var i = start; i <= end; i++){
12605                 nodes.push(ns[i]);
12606             }
12607         } else{
12608             for(var i = start; i >= end; i--){
12609                 nodes.push(ns[i]);
12610             }
12611         }
12612         return nodes;
12613     },
12614
12615     /**
12616      * Finds the index of the passed node
12617      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12618      * @return {Number} The index of the node or -1
12619      */
12620     indexOf : function(node){
12621         node = this.getNode(node);
12622         if(typeof node.nodeIndex == "number"){
12623             return node.nodeIndex;
12624         }
12625         var ns = this.nodes;
12626         for(var i = 0, len = ns.length; i < len; i++){
12627             if(ns[i] == node){
12628                 return i;
12629             }
12630         }
12631         return -1;
12632     }
12633 });
12634 /*
12635  * - LGPL
12636  *
12637  * based on jquery fullcalendar
12638  * 
12639  */
12640
12641 Roo.bootstrap = Roo.bootstrap || {};
12642 /**
12643  * @class Roo.bootstrap.Calendar
12644  * @extends Roo.bootstrap.Component
12645  * Bootstrap Calendar class
12646  * @cfg {Boolean} loadMask (true|false) default false
12647  * @cfg {Object} header generate the user specific header of the calendar, default false
12648
12649  * @constructor
12650  * Create a new Container
12651  * @param {Object} config The config object
12652  */
12653
12654
12655
12656 Roo.bootstrap.Calendar = function(config){
12657     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12658      this.addEvents({
12659         /**
12660              * @event select
12661              * Fires when a date is selected
12662              * @param {DatePicker} this
12663              * @param {Date} date The selected date
12664              */
12665         'select': true,
12666         /**
12667              * @event monthchange
12668              * Fires when the displayed month changes 
12669              * @param {DatePicker} this
12670              * @param {Date} date The selected month
12671              */
12672         'monthchange': true,
12673         /**
12674              * @event evententer
12675              * Fires when mouse over an event
12676              * @param {Calendar} this
12677              * @param {event} Event
12678              */
12679         'evententer': true,
12680         /**
12681              * @event eventleave
12682              * Fires when the mouse leaves an
12683              * @param {Calendar} this
12684              * @param {event}
12685              */
12686         'eventleave': true,
12687         /**
12688              * @event eventclick
12689              * Fires when the mouse click an
12690              * @param {Calendar} this
12691              * @param {event}
12692              */
12693         'eventclick': true
12694         
12695     });
12696
12697 };
12698
12699 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12700     
12701      /**
12702      * @cfg {Number} startDay
12703      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12704      */
12705     startDay : 0,
12706     
12707     loadMask : false,
12708     
12709     header : false,
12710       
12711     getAutoCreate : function(){
12712         
12713         
12714         var fc_button = function(name, corner, style, content ) {
12715             return Roo.apply({},{
12716                 tag : 'span',
12717                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12718                          (corner.length ?
12719                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12720                             ''
12721                         ),
12722                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12723                 unselectable: 'on'
12724             });
12725         };
12726         
12727         var header = {};
12728         
12729         if(!this.header){
12730             header = {
12731                 tag : 'table',
12732                 cls : 'fc-header',
12733                 style : 'width:100%',
12734                 cn : [
12735                     {
12736                         tag: 'tr',
12737                         cn : [
12738                             {
12739                                 tag : 'td',
12740                                 cls : 'fc-header-left',
12741                                 cn : [
12742                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12743                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12744                                     { tag: 'span', cls: 'fc-header-space' },
12745                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12746
12747
12748                                 ]
12749                             },
12750
12751                             {
12752                                 tag : 'td',
12753                                 cls : 'fc-header-center',
12754                                 cn : [
12755                                     {
12756                                         tag: 'span',
12757                                         cls: 'fc-header-title',
12758                                         cn : {
12759                                             tag: 'H2',
12760                                             html : 'month / year'
12761                                         }
12762                                     }
12763
12764                                 ]
12765                             },
12766                             {
12767                                 tag : 'td',
12768                                 cls : 'fc-header-right',
12769                                 cn : [
12770                               /*      fc_button('month', 'left', '', 'month' ),
12771                                     fc_button('week', '', '', 'week' ),
12772                                     fc_button('day', 'right', '', 'day' )
12773                                 */    
12774
12775                                 ]
12776                             }
12777
12778                         ]
12779                     }
12780                 ]
12781             };
12782         }
12783         
12784         header = this.header;
12785         
12786        
12787         var cal_heads = function() {
12788             var ret = [];
12789             // fixme - handle this.
12790             
12791             for (var i =0; i < Date.dayNames.length; i++) {
12792                 var d = Date.dayNames[i];
12793                 ret.push({
12794                     tag: 'th',
12795                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12796                     html : d.substring(0,3)
12797                 });
12798                 
12799             }
12800             ret[0].cls += ' fc-first';
12801             ret[6].cls += ' fc-last';
12802             return ret;
12803         };
12804         var cal_cell = function(n) {
12805             return  {
12806                 tag: 'td',
12807                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12808                 cn : [
12809                     {
12810                         cn : [
12811                             {
12812                                 cls: 'fc-day-number',
12813                                 html: 'D'
12814                             },
12815                             {
12816                                 cls: 'fc-day-content',
12817                              
12818                                 cn : [
12819                                      {
12820                                         style: 'position: relative;' // height: 17px;
12821                                     }
12822                                 ]
12823                             }
12824                             
12825                             
12826                         ]
12827                     }
12828                 ]
12829                 
12830             }
12831         };
12832         var cal_rows = function() {
12833             
12834             var ret = []
12835             for (var r = 0; r < 6; r++) {
12836                 var row= {
12837                     tag : 'tr',
12838                     cls : 'fc-week',
12839                     cn : []
12840                 };
12841                 
12842                 for (var i =0; i < Date.dayNames.length; i++) {
12843                     var d = Date.dayNames[i];
12844                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12845
12846                 }
12847                 row.cn[0].cls+=' fc-first';
12848                 row.cn[0].cn[0].style = 'min-height:90px';
12849                 row.cn[6].cls+=' fc-last';
12850                 ret.push(row);
12851                 
12852             }
12853             ret[0].cls += ' fc-first';
12854             ret[4].cls += ' fc-prev-last';
12855             ret[5].cls += ' fc-last';
12856             return ret;
12857             
12858         };
12859         
12860         var cal_table = {
12861             tag: 'table',
12862             cls: 'fc-border-separate',
12863             style : 'width:100%',
12864             cellspacing  : 0,
12865             cn : [
12866                 { 
12867                     tag: 'thead',
12868                     cn : [
12869                         { 
12870                             tag: 'tr',
12871                             cls : 'fc-first fc-last',
12872                             cn : cal_heads()
12873                         }
12874                     ]
12875                 },
12876                 { 
12877                     tag: 'tbody',
12878                     cn : cal_rows()
12879                 }
12880                   
12881             ]
12882         };
12883          
12884          var cfg = {
12885             cls : 'fc fc-ltr',
12886             cn : [
12887                 header,
12888                 {
12889                     cls : 'fc-content',
12890                     style : "position: relative;",
12891                     cn : [
12892                         {
12893                             cls : 'fc-view fc-view-month fc-grid',
12894                             style : 'position: relative',
12895                             unselectable : 'on',
12896                             cn : [
12897                                 {
12898                                     cls : 'fc-event-container',
12899                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12900                                 },
12901                                 cal_table
12902                             ]
12903                         }
12904                     ]
12905     
12906                 }
12907            ] 
12908             
12909         };
12910         
12911          
12912         
12913         return cfg;
12914     },
12915     
12916     
12917     initEvents : function()
12918     {
12919         if(!this.store){
12920             throw "can not find store for calendar";
12921         }
12922         
12923         var mark = {
12924             tag: "div",
12925             cls:"x-dlg-mask",
12926             style: "text-align:center",
12927             cn: [
12928                 {
12929                     tag: "div",
12930                     style: "background-color:white;width:50%;margin:250 auto",
12931                     cn: [
12932                         {
12933                             tag: "img",
12934                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12935                         },
12936                         {
12937                             tag: "span",
12938                             html: "Loading"
12939                         }
12940                         
12941                     ]
12942                 }
12943             ]
12944         }
12945         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12946         
12947         var size = this.el.select('.fc-content', true).first().getSize();
12948         this.maskEl.setSize(size.width, size.height);
12949         this.maskEl.enableDisplayMode("block");
12950         if(!this.loadMask){
12951             this.maskEl.hide();
12952         }
12953         
12954         this.store = Roo.factory(this.store, Roo.data);
12955         this.store.on('load', this.onLoad, this);
12956         this.store.on('beforeload', this.onBeforeLoad, this);
12957         
12958         this.resize();
12959         
12960         this.cells = this.el.select('.fc-day',true);
12961         //Roo.log(this.cells);
12962         this.textNodes = this.el.query('.fc-day-number');
12963         this.cells.addClassOnOver('fc-state-hover');
12964         
12965         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12966         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12967         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12968         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12969         
12970         this.on('monthchange', this.onMonthChange, this);
12971         
12972         this.update(new Date().clearTime());
12973     },
12974     
12975     resize : function() {
12976         var sz  = this.el.getSize();
12977         
12978         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12979         this.el.select('.fc-day-content div',true).setHeight(34);
12980     },
12981     
12982     
12983     // private
12984     showPrevMonth : function(e){
12985         this.update(this.activeDate.add("mo", -1));
12986     },
12987     showToday : function(e){
12988         this.update(new Date().clearTime());
12989     },
12990     // private
12991     showNextMonth : function(e){
12992         this.update(this.activeDate.add("mo", 1));
12993     },
12994
12995     // private
12996     showPrevYear : function(){
12997         this.update(this.activeDate.add("y", -1));
12998     },
12999
13000     // private
13001     showNextYear : function(){
13002         this.update(this.activeDate.add("y", 1));
13003     },
13004
13005     
13006    // private
13007     update : function(date)
13008     {
13009         var vd = this.activeDate;
13010         this.activeDate = date;
13011 //        if(vd && this.el){
13012 //            var t = date.getTime();
13013 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13014 //                Roo.log('using add remove');
13015 //                
13016 //                this.fireEvent('monthchange', this, date);
13017 //                
13018 //                this.cells.removeClass("fc-state-highlight");
13019 //                this.cells.each(function(c){
13020 //                   if(c.dateValue == t){
13021 //                       c.addClass("fc-state-highlight");
13022 //                       setTimeout(function(){
13023 //                            try{c.dom.firstChild.focus();}catch(e){}
13024 //                       }, 50);
13025 //                       return false;
13026 //                   }
13027 //                   return true;
13028 //                });
13029 //                return;
13030 //            }
13031 //        }
13032         
13033         var days = date.getDaysInMonth();
13034         
13035         var firstOfMonth = date.getFirstDateOfMonth();
13036         var startingPos = firstOfMonth.getDay()-this.startDay;
13037         
13038         if(startingPos < this.startDay){
13039             startingPos += 7;
13040         }
13041         
13042         var pm = date.add(Date.MONTH, -1);
13043         var prevStart = pm.getDaysInMonth()-startingPos;
13044 //        
13045         this.cells = this.el.select('.fc-day',true);
13046         this.textNodes = this.el.query('.fc-day-number');
13047         this.cells.addClassOnOver('fc-state-hover');
13048         
13049         var cells = this.cells.elements;
13050         var textEls = this.textNodes;
13051         
13052         Roo.each(cells, function(cell){
13053             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13054         });
13055         
13056         days += startingPos;
13057
13058         // convert everything to numbers so it's fast
13059         var day = 86400000;
13060         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13061         //Roo.log(d);
13062         //Roo.log(pm);
13063         //Roo.log(prevStart);
13064         
13065         var today = new Date().clearTime().getTime();
13066         var sel = date.clearTime().getTime();
13067         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13068         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13069         var ddMatch = this.disabledDatesRE;
13070         var ddText = this.disabledDatesText;
13071         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13072         var ddaysText = this.disabledDaysText;
13073         var format = this.format;
13074         
13075         var setCellClass = function(cal, cell){
13076             cell.row = 0;
13077             cell.events = [];
13078             cell.more = [];
13079             //Roo.log('set Cell Class');
13080             cell.title = "";
13081             var t = d.getTime();
13082             
13083             //Roo.log(d);
13084             
13085             cell.dateValue = t;
13086             if(t == today){
13087                 cell.className += " fc-today";
13088                 cell.className += " fc-state-highlight";
13089                 cell.title = cal.todayText;
13090             }
13091             if(t == sel){
13092                 // disable highlight in other month..
13093                 //cell.className += " fc-state-highlight";
13094                 
13095             }
13096             // disabling
13097             if(t < min) {
13098                 cell.className = " fc-state-disabled";
13099                 cell.title = cal.minText;
13100                 return;
13101             }
13102             if(t > max) {
13103                 cell.className = " fc-state-disabled";
13104                 cell.title = cal.maxText;
13105                 return;
13106             }
13107             if(ddays){
13108                 if(ddays.indexOf(d.getDay()) != -1){
13109                     cell.title = ddaysText;
13110                     cell.className = " fc-state-disabled";
13111                 }
13112             }
13113             if(ddMatch && format){
13114                 var fvalue = d.dateFormat(format);
13115                 if(ddMatch.test(fvalue)){
13116                     cell.title = ddText.replace("%0", fvalue);
13117                     cell.className = " fc-state-disabled";
13118                 }
13119             }
13120             
13121             if (!cell.initialClassName) {
13122                 cell.initialClassName = cell.dom.className;
13123             }
13124             
13125             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13126         };
13127
13128         var i = 0;
13129         
13130         for(; i < startingPos; i++) {
13131             textEls[i].innerHTML = (++prevStart);
13132             d.setDate(d.getDate()+1);
13133             
13134             cells[i].className = "fc-past fc-other-month";
13135             setCellClass(this, cells[i]);
13136         }
13137         
13138         var intDay = 0;
13139         
13140         for(; i < days; i++){
13141             intDay = i - startingPos + 1;
13142             textEls[i].innerHTML = (intDay);
13143             d.setDate(d.getDate()+1);
13144             
13145             cells[i].className = ''; // "x-date-active";
13146             setCellClass(this, cells[i]);
13147         }
13148         var extraDays = 0;
13149         
13150         for(; i < 42; i++) {
13151             textEls[i].innerHTML = (++extraDays);
13152             d.setDate(d.getDate()+1);
13153             
13154             cells[i].className = "fc-future fc-other-month";
13155             setCellClass(this, cells[i]);
13156         }
13157         
13158         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13159         
13160         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13161         
13162         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13163         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13164         
13165         if(totalRows != 6){
13166             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13167             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13168         }
13169         
13170         this.fireEvent('monthchange', this, date);
13171         
13172         
13173         /*
13174         if(!this.internalRender){
13175             var main = this.el.dom.firstChild;
13176             var w = main.offsetWidth;
13177             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13178             Roo.fly(main).setWidth(w);
13179             this.internalRender = true;
13180             // opera does not respect the auto grow header center column
13181             // then, after it gets a width opera refuses to recalculate
13182             // without a second pass
13183             if(Roo.isOpera && !this.secondPass){
13184                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13185                 this.secondPass = true;
13186                 this.update.defer(10, this, [date]);
13187             }
13188         }
13189         */
13190         
13191     },
13192     
13193     findCell : function(dt) {
13194         dt = dt.clearTime().getTime();
13195         var ret = false;
13196         this.cells.each(function(c){
13197             //Roo.log("check " +c.dateValue + '?=' + dt);
13198             if(c.dateValue == dt){
13199                 ret = c;
13200                 return false;
13201             }
13202             return true;
13203         });
13204         
13205         return ret;
13206     },
13207     
13208     findCells : function(ev) {
13209         var s = ev.start.clone().clearTime().getTime();
13210        // Roo.log(s);
13211         var e= ev.end.clone().clearTime().getTime();
13212        // Roo.log(e);
13213         var ret = [];
13214         this.cells.each(function(c){
13215              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13216             
13217             if(c.dateValue > e){
13218                 return ;
13219             }
13220             if(c.dateValue < s){
13221                 return ;
13222             }
13223             ret.push(c);
13224         });
13225         
13226         return ret;    
13227     },
13228     
13229 //    findBestRow: function(cells)
13230 //    {
13231 //        var ret = 0;
13232 //        
13233 //        for (var i =0 ; i < cells.length;i++) {
13234 //            ret  = Math.max(cells[i].rows || 0,ret);
13235 //        }
13236 //        return ret;
13237 //        
13238 //    },
13239     
13240     
13241     addItem : function(ev)
13242     {
13243         // look for vertical location slot in
13244         var cells = this.findCells(ev);
13245         
13246 //        ev.row = this.findBestRow(cells);
13247         
13248         // work out the location.
13249         
13250         var crow = false;
13251         var rows = [];
13252         for(var i =0; i < cells.length; i++) {
13253             
13254             cells[i].row = cells[0].row;
13255             
13256             if(i == 0){
13257                 cells[i].row = cells[i].row + 1;
13258             }
13259             
13260             if (!crow) {
13261                 crow = {
13262                     start : cells[i],
13263                     end :  cells[i]
13264                 };
13265                 continue;
13266             }
13267             if (crow.start.getY() == cells[i].getY()) {
13268                 // on same row.
13269                 crow.end = cells[i];
13270                 continue;
13271             }
13272             // different row.
13273             rows.push(crow);
13274             crow = {
13275                 start: cells[i],
13276                 end : cells[i]
13277             };
13278             
13279         }
13280         
13281         rows.push(crow);
13282         ev.els = [];
13283         ev.rows = rows;
13284         ev.cells = cells;
13285         
13286         cells[0].events.push(ev);
13287         
13288         this.calevents.push(ev);
13289     },
13290     
13291     clearEvents: function() {
13292         
13293         if(!this.calevents){
13294             return;
13295         }
13296         
13297         Roo.each(this.cells.elements, function(c){
13298             c.row = 0;
13299             c.events = [];
13300             c.more = [];
13301         });
13302         
13303         Roo.each(this.calevents, function(e) {
13304             Roo.each(e.els, function(el) {
13305                 el.un('mouseenter' ,this.onEventEnter, this);
13306                 el.un('mouseleave' ,this.onEventLeave, this);
13307                 el.remove();
13308             },this);
13309         },this);
13310         
13311         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13312             e.remove();
13313         });
13314         
13315     },
13316     
13317     renderEvents: function()
13318     {   
13319         var _this = this;
13320         
13321         this.cells.each(function(c) {
13322             
13323             if(c.row < 5){
13324                 return;
13325             }
13326             
13327             var ev = c.events;
13328             
13329             var r = 4;
13330             if(c.row != c.events.length){
13331                 r = 4 - (4 - (c.row - c.events.length));
13332             }
13333             
13334             c.events = ev.slice(0, r);
13335             c.more = ev.slice(r);
13336             
13337             if(c.more.length && c.more.length == 1){
13338                 c.events.push(c.more.pop());
13339             }
13340             
13341             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13342             
13343         });
13344             
13345         this.cells.each(function(c) {
13346             
13347             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13348             
13349             
13350             for (var e = 0; e < c.events.length; e++){
13351                 var ev = c.events[e];
13352                 var rows = ev.rows;
13353                 
13354                 for(var i = 0; i < rows.length; i++) {
13355                 
13356                     // how many rows should it span..
13357
13358                     var  cfg = {
13359                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13360                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13361
13362                         unselectable : "on",
13363                         cn : [
13364                             {
13365                                 cls: 'fc-event-inner',
13366                                 cn : [
13367     //                                {
13368     //                                  tag:'span',
13369     //                                  cls: 'fc-event-time',
13370     //                                  html : cells.length > 1 ? '' : ev.time
13371     //                                },
13372                                     {
13373                                       tag:'span',
13374                                       cls: 'fc-event-title',
13375                                       html : String.format('{0}', ev.title)
13376                                     }
13377
13378
13379                                 ]
13380                             },
13381                             {
13382                                 cls: 'ui-resizable-handle ui-resizable-e',
13383                                 html : '&nbsp;&nbsp;&nbsp'
13384                             }
13385
13386                         ]
13387                     };
13388
13389                     if (i == 0) {
13390                         cfg.cls += ' fc-event-start';
13391                     }
13392                     if ((i+1) == rows.length) {
13393                         cfg.cls += ' fc-event-end';
13394                     }
13395
13396                     var ctr = _this.el.select('.fc-event-container',true).first();
13397                     var cg = ctr.createChild(cfg);
13398
13399                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13400                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13401
13402                     var r = (c.more.length) ? 1 : 0;
13403                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13404                     cg.setWidth(ebox.right - sbox.x -2);
13405
13406                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13407                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13408                     cg.on('click', _this.onEventClick, _this, ev);
13409
13410                     ev.els.push(cg);
13411                     
13412                 }
13413                 
13414             }
13415             
13416             
13417             if(c.more.length){
13418                 var  cfg = {
13419                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13420                     style : 'position: absolute',
13421                     unselectable : "on",
13422                     cn : [
13423                         {
13424                             cls: 'fc-event-inner',
13425                             cn : [
13426                                 {
13427                                   tag:'span',
13428                                   cls: 'fc-event-title',
13429                                   html : 'More'
13430                                 }
13431
13432
13433                             ]
13434                         },
13435                         {
13436                             cls: 'ui-resizable-handle ui-resizable-e',
13437                             html : '&nbsp;&nbsp;&nbsp'
13438                         }
13439
13440                     ]
13441                 };
13442
13443                 var ctr = _this.el.select('.fc-event-container',true).first();
13444                 var cg = ctr.createChild(cfg);
13445
13446                 var sbox = c.select('.fc-day-content',true).first().getBox();
13447                 var ebox = c.select('.fc-day-content',true).first().getBox();
13448                 //Roo.log(cg);
13449                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13450                 cg.setWidth(ebox.right - sbox.x -2);
13451
13452                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13453                 
13454             }
13455             
13456         });
13457         
13458         
13459         
13460     },
13461     
13462     onEventEnter: function (e, el,event,d) {
13463         this.fireEvent('evententer', this, el, event);
13464     },
13465     
13466     onEventLeave: function (e, el,event,d) {
13467         this.fireEvent('eventleave', this, el, event);
13468     },
13469     
13470     onEventClick: function (e, el,event,d) {
13471         this.fireEvent('eventclick', this, el, event);
13472     },
13473     
13474     onMonthChange: function () {
13475         this.store.load();
13476     },
13477     
13478     onMoreEventClick: function(e, el, more)
13479     {
13480         var _this = this;
13481         
13482         this.calpopover.placement = 'right';
13483         this.calpopover.setTitle('More');
13484         
13485         this.calpopover.setContent('');
13486         
13487         var ctr = this.calpopover.el.select('.popover-content', true).first();
13488         
13489         Roo.each(more, function(m){
13490             var cfg = {
13491                 cls : 'fc-event-hori fc-event-draggable',
13492                 html : m.title
13493             }
13494             var cg = ctr.createChild(cfg);
13495             
13496             cg.on('click', _this.onEventClick, _this, m);
13497         });
13498         
13499         this.calpopover.show(el);
13500         
13501         
13502     },
13503     
13504     onLoad: function () 
13505     {   
13506         this.calevents = [];
13507         var cal = this;
13508         
13509         if(this.store.getCount() > 0){
13510             this.store.data.each(function(d){
13511                cal.addItem({
13512                     id : d.data.id,
13513                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13514                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13515                     time : d.data.start_time,
13516                     title : d.data.title,
13517                     description : d.data.description,
13518                     venue : d.data.venue
13519                 });
13520             });
13521         }
13522         
13523         this.renderEvents();
13524         
13525         if(this.calevents.length && this.loadMask){
13526             this.maskEl.hide();
13527         }
13528     },
13529     
13530     onBeforeLoad: function()
13531     {
13532         this.clearEvents();
13533         if(this.loadMask){
13534             this.maskEl.show();
13535         }
13536     }
13537 });
13538
13539  
13540  /*
13541  * - LGPL
13542  *
13543  * element
13544  * 
13545  */
13546
13547 /**
13548  * @class Roo.bootstrap.Popover
13549  * @extends Roo.bootstrap.Component
13550  * Bootstrap Popover class
13551  * @cfg {String} html contents of the popover   (or false to use children..)
13552  * @cfg {String} title of popover (or false to hide)
13553  * @cfg {String} placement how it is placed
13554  * @cfg {String} trigger click || hover (or false to trigger manually)
13555  * @cfg {String} over what (parent or false to trigger manually.)
13556  * 
13557  * @constructor
13558  * Create a new Popover
13559  * @param {Object} config The config object
13560  */
13561
13562 Roo.bootstrap.Popover = function(config){
13563     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13564 };
13565
13566 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13567     
13568     title: 'Fill in a title',
13569     html: false,
13570     
13571     placement : 'right',
13572     trigger : 'hover', // hover
13573     
13574     over: 'parent',
13575     
13576     can_build_overlaid : false,
13577     
13578     getChildContainer : function()
13579     {
13580         return this.el.select('.popover-content',true).first();
13581     },
13582     
13583     getAutoCreate : function(){
13584          Roo.log('make popover?');
13585         var cfg = {
13586            cls : 'popover roo-dynamic',
13587            style: 'display:block',
13588            cn : [
13589                 {
13590                     cls : 'arrow'
13591                 },
13592                 {
13593                     cls : 'popover-inner',
13594                     cn : [
13595                         {
13596                             tag: 'h3',
13597                             cls: 'popover-title',
13598                             html : this.title
13599                         },
13600                         {
13601                             cls : 'popover-content',
13602                             html : this.html
13603                         }
13604                     ]
13605                     
13606                 }
13607            ]
13608         };
13609         
13610         return cfg;
13611     },
13612     setTitle: function(str)
13613     {
13614         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13615     },
13616     setContent: function(str)
13617     {
13618         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13619     },
13620     // as it get's added to the bottom of the page.
13621     onRender : function(ct, position)
13622     {
13623         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13624         if(!this.el){
13625             var cfg = Roo.apply({},  this.getAutoCreate());
13626             cfg.id = Roo.id();
13627             
13628             if (this.cls) {
13629                 cfg.cls += ' ' + this.cls;
13630             }
13631             if (this.style) {
13632                 cfg.style = this.style;
13633             }
13634             Roo.log("adding to ")
13635             this.el = Roo.get(document.body).createChild(cfg, position);
13636             Roo.log(this.el);
13637         }
13638         this.initEvents();
13639     },
13640     
13641     initEvents : function()
13642     {
13643         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13644         this.el.enableDisplayMode('block');
13645         this.el.hide();
13646         if (this.over === false) {
13647             return; 
13648         }
13649         if (this.triggers === false) {
13650             return;
13651         }
13652         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13653         var triggers = this.trigger ? this.trigger.split(' ') : [];
13654         Roo.each(triggers, function(trigger) {
13655         
13656             if (trigger == 'click') {
13657                 on_el.on('click', this.toggle, this);
13658             } else if (trigger != 'manual') {
13659                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13660                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13661       
13662                 on_el.on(eventIn  ,this.enter, this);
13663                 on_el.on(eventOut, this.leave, this);
13664             }
13665         }, this);
13666         
13667     },
13668     
13669     
13670     // private
13671     timeout : null,
13672     hoverState : null,
13673     
13674     toggle : function () {
13675         this.hoverState == 'in' ? this.leave() : this.enter();
13676     },
13677     
13678     enter : function () {
13679        
13680     
13681         clearTimeout(this.timeout);
13682     
13683         this.hoverState = 'in'
13684     
13685         if (!this.delay || !this.delay.show) {
13686             this.show();
13687             return 
13688         }
13689         var _t = this;
13690         this.timeout = setTimeout(function () {
13691             if (_t.hoverState == 'in') {
13692                 _t.show();
13693             }
13694         }, this.delay.show)
13695     },
13696     leave : function() {
13697         clearTimeout(this.timeout);
13698     
13699         this.hoverState = 'out'
13700     
13701         if (!this.delay || !this.delay.hide) {
13702             this.hide();
13703             return 
13704         }
13705         var _t = this;
13706         this.timeout = setTimeout(function () {
13707             if (_t.hoverState == 'out') {
13708                 _t.hide();
13709             }
13710         }, this.delay.hide)
13711     },
13712     
13713     show : function (on_el)
13714     {
13715         if (!on_el) {
13716             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13717         }
13718         // set content.
13719         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13720         if (this.html !== false) {
13721             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13722         }
13723         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13724         if (!this.title.length) {
13725             this.el.select('.popover-title',true).hide();
13726         }
13727         
13728         var placement = typeof this.placement == 'function' ?
13729             this.placement.call(this, this.el, on_el) :
13730             this.placement;
13731             
13732         var autoToken = /\s?auto?\s?/i;
13733         var autoPlace = autoToken.test(placement);
13734         if (autoPlace) {
13735             placement = placement.replace(autoToken, '') || 'top';
13736         }
13737         
13738         //this.el.detach()
13739         //this.el.setXY([0,0]);
13740         this.el.show();
13741         this.el.dom.style.display='block';
13742         this.el.addClass(placement);
13743         
13744         //this.el.appendTo(on_el);
13745         
13746         var p = this.getPosition();
13747         var box = this.el.getBox();
13748         
13749         if (autoPlace) {
13750             // fixme..
13751         }
13752         var align = Roo.bootstrap.Popover.alignment[placement]
13753         this.el.alignTo(on_el, align[0],align[1]);
13754         //var arrow = this.el.select('.arrow',true).first();
13755         //arrow.set(align[2], 
13756         
13757         this.el.addClass('in');
13758         this.hoverState = null;
13759         
13760         if (this.el.hasClass('fade')) {
13761             // fade it?
13762         }
13763         
13764     },
13765     hide : function()
13766     {
13767         this.el.setXY([0,0]);
13768         this.el.removeClass('in');
13769         this.el.hide();
13770         
13771     }
13772     
13773 });
13774
13775 Roo.bootstrap.Popover.alignment = {
13776     'left' : ['r-l', [-10,0], 'right'],
13777     'right' : ['l-r', [10,0], 'left'],
13778     'bottom' : ['t-b', [0,10], 'top'],
13779     'top' : [ 'b-t', [0,-10], 'bottom']
13780 };
13781
13782  /*
13783  * - LGPL
13784  *
13785  * Progress
13786  * 
13787  */
13788
13789 /**
13790  * @class Roo.bootstrap.Progress
13791  * @extends Roo.bootstrap.Component
13792  * Bootstrap Progress class
13793  * @cfg {Boolean} striped striped of the progress bar
13794  * @cfg {Boolean} active animated of the progress bar
13795  * 
13796  * 
13797  * @constructor
13798  * Create a new Progress
13799  * @param {Object} config The config object
13800  */
13801
13802 Roo.bootstrap.Progress = function(config){
13803     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13804 };
13805
13806 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13807     
13808     striped : false,
13809     active: false,
13810     
13811     getAutoCreate : function(){
13812         var cfg = {
13813             tag: 'div',
13814             cls: 'progress'
13815         };
13816         
13817         
13818         if(this.striped){
13819             cfg.cls += ' progress-striped';
13820         }
13821       
13822         if(this.active){
13823             cfg.cls += ' active';
13824         }
13825         
13826         
13827         return cfg;
13828     }
13829    
13830 });
13831
13832  
13833
13834  /*
13835  * - LGPL
13836  *
13837  * ProgressBar
13838  * 
13839  */
13840
13841 /**
13842  * @class Roo.bootstrap.ProgressBar
13843  * @extends Roo.bootstrap.Component
13844  * Bootstrap ProgressBar class
13845  * @cfg {Number} aria_valuenow aria-value now
13846  * @cfg {Number} aria_valuemin aria-value min
13847  * @cfg {Number} aria_valuemax aria-value max
13848  * @cfg {String} label label for the progress bar
13849  * @cfg {String} panel (success | info | warning | danger )
13850  * @cfg {String} role role of the progress bar
13851  * @cfg {String} sr_only text
13852  * 
13853  * 
13854  * @constructor
13855  * Create a new ProgressBar
13856  * @param {Object} config The config object
13857  */
13858
13859 Roo.bootstrap.ProgressBar = function(config){
13860     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13861 };
13862
13863 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13864     
13865     aria_valuenow : 0,
13866     aria_valuemin : 0,
13867     aria_valuemax : 100,
13868     label : false,
13869     panel : false,
13870     role : false,
13871     sr_only: false,
13872     
13873     getAutoCreate : function()
13874     {
13875         
13876         var cfg = {
13877             tag: 'div',
13878             cls: 'progress-bar',
13879             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13880         };
13881         
13882         if(this.sr_only){
13883             cfg.cn = {
13884                 tag: 'span',
13885                 cls: 'sr-only',
13886                 html: this.sr_only
13887             }
13888         }
13889         
13890         if(this.role){
13891             cfg.role = this.role;
13892         }
13893         
13894         if(this.aria_valuenow){
13895             cfg['aria-valuenow'] = this.aria_valuenow;
13896         }
13897         
13898         if(this.aria_valuemin){
13899             cfg['aria-valuemin'] = this.aria_valuemin;
13900         }
13901         
13902         if(this.aria_valuemax){
13903             cfg['aria-valuemax'] = this.aria_valuemax;
13904         }
13905         
13906         if(this.label && !this.sr_only){
13907             cfg.html = this.label;
13908         }
13909         
13910         if(this.panel){
13911             cfg.cls += ' progress-bar-' + this.panel;
13912         }
13913         
13914         return cfg;
13915     },
13916     
13917     update : function(aria_valuenow)
13918     {
13919         this.aria_valuenow = aria_valuenow;
13920         
13921         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13922     }
13923    
13924 });
13925
13926  
13927
13928  /*
13929  * - LGPL
13930  *
13931  * column
13932  * 
13933  */
13934
13935 /**
13936  * @class Roo.bootstrap.TabGroup
13937  * @extends Roo.bootstrap.Column
13938  * Bootstrap Column class
13939  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13940  * @cfg {Boolean} carousel true to make the group behave like a carousel
13941  * 
13942  * @constructor
13943  * Create a new TabGroup
13944  * @param {Object} config The config object
13945  */
13946
13947 Roo.bootstrap.TabGroup = function(config){
13948     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13949     if (!this.navId) {
13950         this.navId = Roo.id();
13951     }
13952     this.tabs = [];
13953     Roo.bootstrap.TabGroup.register(this);
13954     
13955 };
13956
13957 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13958     
13959     carousel : false,
13960     transition : false,
13961      
13962     getAutoCreate : function()
13963     {
13964         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13965         
13966         cfg.cls += ' tab-content';
13967         
13968         if (this.carousel) {
13969             cfg.cls += ' carousel slide';
13970             cfg.cn = [{
13971                cls : 'carousel-inner'
13972             }]
13973         }
13974         
13975         
13976         return cfg;
13977     },
13978     getChildContainer : function()
13979     {
13980         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
13981     },
13982     
13983     /**
13984     * register a Navigation item
13985     * @param {Roo.bootstrap.NavItem} the navitem to add
13986     */
13987     register : function(item)
13988     {
13989         this.tabs.push( item);
13990         item.navId = this.navId; // not really needed..
13991     
13992     },
13993     
13994     getActivePanel : function()
13995     {
13996         var r = false;
13997         Roo.each(this.tabs, function(t) {
13998             if (t.active) {
13999                 r = t;
14000                 return false;
14001             }
14002             return null;
14003         });
14004         return r;
14005         
14006     },
14007     getPanelByName : function(n)
14008     {
14009         var r = false;
14010         Roo.each(this.tabs, function(t) {
14011             if (t.tabId == n) {
14012                 r = t;
14013                 return false;
14014             }
14015             return null;
14016         });
14017         return r;
14018     },
14019     indexOfPanel : function(p)
14020     {
14021         var r = false;
14022         Roo.each(this.tabs, function(t,i) {
14023             if (t.tabId == p.tabId) {
14024                 r = i;
14025                 return false;
14026             }
14027             return null;
14028         });
14029         return r;
14030     },
14031     /**
14032      * show a specific panel
14033      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14034      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14035      */
14036     showPanel : function (pan)
14037     {
14038         
14039         if (typeof(pan) == 'number') {
14040             pan = this.tabs[pan];
14041         }
14042         if (typeof(pan) == 'string') {
14043             pan = this.getPanelByName(pan);
14044         }
14045         if (pan.tabId == this.getActivePanel().tabId) {
14046             return true;
14047         }
14048         var cur = this.getActivePanel();
14049         
14050         if (false === cur.fireEvent('beforedeactivate')) {
14051             return false;
14052         }
14053         
14054         if (this.carousel) {
14055             this.transition = true;
14056             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14057             var lr = dir == 'next' ? 'left' : 'right';
14058             pan.el.addClass(dir); // or prev
14059             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14060             cur.el.addClass(lr); // or right
14061             pan.el.addClass(lr);
14062             
14063             var _this = this;
14064             cur.el.on('transitionend', function() {
14065                 Roo.log("trans end?");
14066                 
14067                 pan.el.removeClass([lr,dir]);
14068                 pan.setActive(true);
14069                 
14070                 cur.el.removeClass([lr]);
14071                 cur.setActive(false);
14072                 
14073                 _this.transition = false;
14074                 
14075             }, this, { single:  true } );
14076             return true;
14077         }
14078         
14079         cur.setActive(false);
14080         pan.setActive(true);
14081         return true;
14082         
14083     },
14084     showPanelNext : function()
14085     {
14086         var i = this.indexOfPanel(this.getActivePanel());
14087         if (i > this.tabs.length) {
14088             return;
14089         }
14090         this.showPanel(this.tabs[i+1]);
14091     },
14092     showPanelPrev : function()
14093     {
14094         var i = this.indexOfPanel(this.getActivePanel());
14095         if (i  < 1) {
14096             return;
14097         }
14098         this.showPanel(this.tabs[i-1]);
14099     }
14100     
14101     
14102   
14103 });
14104
14105  
14106
14107  
14108  
14109 Roo.apply(Roo.bootstrap.TabGroup, {
14110     
14111     groups: {},
14112      /**
14113     * register a Navigation Group
14114     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14115     */
14116     register : function(navgrp)
14117     {
14118         this.groups[navgrp.navId] = navgrp;
14119         
14120     },
14121     /**
14122     * fetch a Navigation Group based on the navigation ID
14123     * if one does not exist , it will get created.
14124     * @param {string} the navgroup to add
14125     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14126     */
14127     get: function(navId) {
14128         if (typeof(this.groups[navId]) == 'undefined') {
14129             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14130         }
14131         return this.groups[navId] ;
14132     }
14133     
14134     
14135     
14136 });
14137
14138  /*
14139  * - LGPL
14140  *
14141  * TabPanel
14142  * 
14143  */
14144
14145 /**
14146  * @class Roo.bootstrap.TabPanel
14147  * @extends Roo.bootstrap.Component
14148  * Bootstrap TabPanel class
14149  * @cfg {Boolean} active panel active
14150  * @cfg {String} html panel content
14151  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14152  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14153  * 
14154  * 
14155  * @constructor
14156  * Create a new TabPanel
14157  * @param {Object} config The config object
14158  */
14159
14160 Roo.bootstrap.TabPanel = function(config){
14161     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14162     this.addEvents({
14163         /**
14164              * @event changed
14165              * Fires when the active status changes
14166              * @param {Roo.bootstrap.TabPanel} this
14167              * @param {Boolean} state the new state
14168             
14169          */
14170         'changed': true,
14171         /**
14172              * @event beforedeactivate
14173              * Fires before a tab is de-activated - can be used to do validation on a form.
14174              * @param {Roo.bootstrap.TabPanel} this
14175              * @return {Boolean} false if there is an error
14176             
14177          */
14178         'beforedeactivate': true
14179      });
14180     
14181     this.tabId = this.tabId || Roo.id();
14182   
14183 };
14184
14185 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14186     
14187     active: false,
14188     html: false,
14189     tabId: false,
14190     navId : false,
14191     
14192     getAutoCreate : function(){
14193         var cfg = {
14194             tag: 'div',
14195             // item is needed for carousel - not sure if it has any effect otherwise
14196             cls: 'tab-pane item',
14197             html: this.html || ''
14198         };
14199         
14200         if(this.active){
14201             cfg.cls += ' active';
14202         }
14203         
14204         if(this.tabId){
14205             cfg.tabId = this.tabId;
14206         }
14207         
14208         
14209         return cfg;
14210     },
14211     
14212     initEvents:  function()
14213     {
14214         Roo.log('-------- init events on tab panel ---------');
14215         
14216         var p = this.parent();
14217         this.navId = this.navId || p.navId;
14218         
14219         if (typeof(this.navId) != 'undefined') {
14220             // not really needed.. but just in case.. parent should be a NavGroup.
14221             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14222             Roo.log(['register', tg, this]);
14223             tg.register(this);
14224         }
14225     },
14226     
14227     
14228     onRender : function(ct, position)
14229     {
14230        // Roo.log("Call onRender: " + this.xtype);
14231         
14232         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14233         
14234         
14235         
14236         
14237         
14238     },
14239     
14240     setActive: function(state)
14241     {
14242         Roo.log("panel - set active " + this.tabId + "=" + state);
14243         
14244         this.active = state;
14245         if (!state) {
14246             this.el.removeClass('active');
14247             
14248         } else  if (!this.el.hasClass('active')) {
14249             this.el.addClass('active');
14250         }
14251         this.fireEvent('changed', this, state);
14252     }
14253     
14254     
14255 });
14256  
14257
14258  
14259
14260  /*
14261  * - LGPL
14262  *
14263  * DateField
14264  * 
14265  */
14266
14267 /**
14268  * @class Roo.bootstrap.DateField
14269  * @extends Roo.bootstrap.Input
14270  * Bootstrap DateField class
14271  * @cfg {Number} weekStart default 0
14272  * @cfg {Number} weekStart default 0
14273  * @cfg {Number} viewMode default empty, (months|years)
14274  * @cfg {Number} minViewMode default empty, (months|years)
14275  * @cfg {Number} startDate default -Infinity
14276  * @cfg {Number} endDate default Infinity
14277  * @cfg {Boolean} todayHighlight default false
14278  * @cfg {Boolean} todayBtn default false
14279  * @cfg {Boolean} calendarWeeks default false
14280  * @cfg {Object} daysOfWeekDisabled default empty
14281  * 
14282  * @cfg {Boolean} keyboardNavigation default true
14283  * @cfg {String} language default en
14284  * 
14285  * @constructor
14286  * Create a new DateField
14287  * @param {Object} config The config object
14288  */
14289
14290 Roo.bootstrap.DateField = function(config){
14291     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14292      this.addEvents({
14293             /**
14294              * @event show
14295              * Fires when this field show.
14296              * @param {Roo.bootstrap.DateField} this
14297              * @param {Mixed} date The date value
14298              */
14299             show : true,
14300             /**
14301              * @event show
14302              * Fires when this field hide.
14303              * @param {Roo.bootstrap.DateField} this
14304              * @param {Mixed} date The date value
14305              */
14306             hide : true,
14307             /**
14308              * @event select
14309              * Fires when select a date.
14310              * @param {Roo.bootstrap.DateField} this
14311              * @param {Mixed} date The date value
14312              */
14313             select : true
14314         });
14315 };
14316
14317 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14318     
14319     /**
14320      * @cfg {String} format
14321      * The default date format string which can be overriden for localization support.  The format must be
14322      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14323      */
14324     format : "m/d/y",
14325     /**
14326      * @cfg {String} altFormats
14327      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14328      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14329      */
14330     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14331     
14332     weekStart : 0,
14333     
14334     viewMode : '',
14335     
14336     minViewMode : '',
14337     
14338     todayHighlight : false,
14339     
14340     todayBtn: false,
14341     
14342     language: 'en',
14343     
14344     keyboardNavigation: true,
14345     
14346     calendarWeeks: false,
14347     
14348     startDate: -Infinity,
14349     
14350     endDate: Infinity,
14351     
14352     daysOfWeekDisabled: [],
14353     
14354     _events: [],
14355     
14356     UTCDate: function()
14357     {
14358         return new Date(Date.UTC.apply(Date, arguments));
14359     },
14360     
14361     UTCToday: function()
14362     {
14363         var today = new Date();
14364         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14365     },
14366     
14367     getDate: function() {
14368             var d = this.getUTCDate();
14369             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14370     },
14371     
14372     getUTCDate: function() {
14373             return this.date;
14374     },
14375     
14376     setDate: function(d) {
14377             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14378     },
14379     
14380     setUTCDate: function(d) {
14381             this.date = d;
14382             this.setValue(this.formatDate(this.date));
14383     },
14384         
14385     onRender: function(ct, position)
14386     {
14387         
14388         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14389         
14390         this.language = this.language || 'en';
14391         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14392         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14393         
14394         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14395         this.format = this.format || 'm/d/y';
14396         this.isInline = false;
14397         this.isInput = true;
14398         this.component = this.el.select('.add-on', true).first() || false;
14399         this.component = (this.component && this.component.length === 0) ? false : this.component;
14400         this.hasInput = this.component && this.inputEL().length;
14401         
14402         if (typeof(this.minViewMode === 'string')) {
14403             switch (this.minViewMode) {
14404                 case 'months':
14405                     this.minViewMode = 1;
14406                     break;
14407                 case 'years':
14408                     this.minViewMode = 2;
14409                     break;
14410                 default:
14411                     this.minViewMode = 0;
14412                     break;
14413             }
14414         }
14415         
14416         if (typeof(this.viewMode === 'string')) {
14417             switch (this.viewMode) {
14418                 case 'months':
14419                     this.viewMode = 1;
14420                     break;
14421                 case 'years':
14422                     this.viewMode = 2;
14423                     break;
14424                 default:
14425                     this.viewMode = 0;
14426                     break;
14427             }
14428         }
14429                 
14430         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14431         
14432 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14433         
14434         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14435         
14436         this.picker().on('mousedown', this.onMousedown, this);
14437         this.picker().on('click', this.onClick, this);
14438         
14439         this.picker().addClass('datepicker-dropdown');
14440         
14441         this.startViewMode = this.viewMode;
14442         
14443         
14444         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14445             if(!this.calendarWeeks){
14446                 v.remove();
14447                 return;
14448             };
14449             
14450             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14451             v.attr('colspan', function(i, val){
14452                 return parseInt(val) + 1;
14453             });
14454         })
14455                         
14456         
14457         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14458         
14459         this.setStartDate(this.startDate);
14460         this.setEndDate(this.endDate);
14461         
14462         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14463         
14464         this.fillDow();
14465         this.fillMonths();
14466         this.update();
14467         this.showMode();
14468         
14469         if(this.isInline) {
14470             this.show();
14471         }
14472     },
14473     
14474     picker : function()
14475     {
14476         return this.pickerEl;
14477 //        return this.el.select('.datepicker', true).first();
14478     },
14479     
14480     fillDow: function()
14481     {
14482         var dowCnt = this.weekStart;
14483         
14484         var dow = {
14485             tag: 'tr',
14486             cn: [
14487                 
14488             ]
14489         };
14490         
14491         if(this.calendarWeeks){
14492             dow.cn.push({
14493                 tag: 'th',
14494                 cls: 'cw',
14495                 html: '&nbsp;'
14496             })
14497         }
14498         
14499         while (dowCnt < this.weekStart + 7) {
14500             dow.cn.push({
14501                 tag: 'th',
14502                 cls: 'dow',
14503                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14504             });
14505         }
14506         
14507         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14508     },
14509     
14510     fillMonths: function()
14511     {    
14512         var i = 0
14513         var months = this.picker().select('>.datepicker-months td', true).first();
14514         
14515         months.dom.innerHTML = '';
14516         
14517         while (i < 12) {
14518             var month = {
14519                 tag: 'span',
14520                 cls: 'month',
14521                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14522             }
14523             
14524             months.createChild(month);
14525         }
14526         
14527     },
14528     
14529     update: function()
14530     {
14531         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14532         
14533         if (this.date < this.startDate) {
14534             this.viewDate = new Date(this.startDate);
14535         } else if (this.date > this.endDate) {
14536             this.viewDate = new Date(this.endDate);
14537         } else {
14538             this.viewDate = new Date(this.date);
14539         }
14540         
14541         this.fill();
14542     },
14543     
14544     fill: function() 
14545     {
14546         var d = new Date(this.viewDate),
14547                 year = d.getUTCFullYear(),
14548                 month = d.getUTCMonth(),
14549                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14550                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14551                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14552                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14553                 currentDate = this.date && this.date.valueOf(),
14554                 today = this.UTCToday();
14555         
14556         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14557         
14558 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14559         
14560 //        this.picker.select('>tfoot th.today').
14561 //                                              .text(dates[this.language].today)
14562 //                                              .toggle(this.todayBtn !== false);
14563     
14564         this.updateNavArrows();
14565         this.fillMonths();
14566                                                 
14567         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14568         
14569         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14570          
14571         prevMonth.setUTCDate(day);
14572         
14573         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14574         
14575         var nextMonth = new Date(prevMonth);
14576         
14577         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14578         
14579         nextMonth = nextMonth.valueOf();
14580         
14581         var fillMonths = false;
14582         
14583         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14584         
14585         while(prevMonth.valueOf() < nextMonth) {
14586             var clsName = '';
14587             
14588             if (prevMonth.getUTCDay() === this.weekStart) {
14589                 if(fillMonths){
14590                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14591                 }
14592                     
14593                 fillMonths = {
14594                     tag: 'tr',
14595                     cn: []
14596                 };
14597                 
14598                 if(this.calendarWeeks){
14599                     // ISO 8601: First week contains first thursday.
14600                     // ISO also states week starts on Monday, but we can be more abstract here.
14601                     var
14602                     // Start of current week: based on weekstart/current date
14603                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14604                     // Thursday of this week
14605                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14606                     // First Thursday of year, year from thursday
14607                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14608                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14609                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14610                     
14611                     fillMonths.cn.push({
14612                         tag: 'td',
14613                         cls: 'cw',
14614                         html: calWeek
14615                     });
14616                 }
14617             }
14618             
14619             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14620                 clsName += ' old';
14621             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14622                 clsName += ' new';
14623             }
14624             if (this.todayHighlight &&
14625                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14626                 prevMonth.getUTCMonth() == today.getMonth() &&
14627                 prevMonth.getUTCDate() == today.getDate()) {
14628                 clsName += ' today';
14629             }
14630             
14631             if (currentDate && prevMonth.valueOf() === currentDate) {
14632                 clsName += ' active';
14633             }
14634             
14635             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14636                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14637                     clsName += ' disabled';
14638             }
14639             
14640             fillMonths.cn.push({
14641                 tag: 'td',
14642                 cls: 'day ' + clsName,
14643                 html: prevMonth.getDate()
14644             })
14645             
14646             prevMonth.setDate(prevMonth.getDate()+1);
14647         }
14648           
14649         var currentYear = this.date && this.date.getUTCFullYear();
14650         var currentMonth = this.date && this.date.getUTCMonth();
14651         
14652         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14653         
14654         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14655             v.removeClass('active');
14656             
14657             if(currentYear === year && k === currentMonth){
14658                 v.addClass('active');
14659             }
14660             
14661             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14662                 v.addClass('disabled');
14663             }
14664             
14665         });
14666         
14667         
14668         year = parseInt(year/10, 10) * 10;
14669         
14670         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14671         
14672         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14673         
14674         year -= 1;
14675         for (var i = -1; i < 11; i++) {
14676             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14677                 tag: 'span',
14678                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14679                 html: year
14680             })
14681             
14682             year += 1;
14683         }
14684     },
14685     
14686     showMode: function(dir) 
14687     {
14688         if (dir) {
14689             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14690         }
14691         Roo.each(this.picker().select('>div',true).elements, function(v){
14692             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14693             v.hide();
14694         });
14695         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14696     },
14697     
14698     place: function()
14699     {
14700         if(this.isInline) return;
14701         
14702         this.picker().removeClass(['bottom', 'top']);
14703         
14704         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14705             /*
14706              * place to the top of element!
14707              *
14708              */
14709             
14710             this.picker().addClass('top');
14711             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14712             
14713             return;
14714         }
14715         
14716         this.picker().addClass('bottom');
14717         
14718         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14719     },
14720     
14721     parseDate : function(value)
14722     {
14723         if(!value || value instanceof Date){
14724             return value;
14725         }
14726         var v = Date.parseDate(value, this.format);
14727         if (!v && this.useIso) {
14728             v = Date.parseDate(value, 'Y-m-d');
14729         }
14730         if(!v && this.altFormats){
14731             if(!this.altFormatsArray){
14732                 this.altFormatsArray = this.altFormats.split("|");
14733             }
14734             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14735                 v = Date.parseDate(value, this.altFormatsArray[i]);
14736             }
14737         }
14738         return v;
14739     },
14740     
14741     formatDate : function(date, fmt)
14742     {
14743         return (!date || !(date instanceof Date)) ?
14744         date : date.dateFormat(fmt || this.format);
14745     },
14746     
14747     onFocus : function()
14748     {
14749         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14750         this.show();
14751     },
14752     
14753     onBlur : function()
14754     {
14755         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14756         
14757         var d = this.inputEl().getValue();
14758         
14759         this.setValue(d);
14760                 
14761         this.hide();
14762     },
14763     
14764     show : function()
14765     {
14766         this.picker().show();
14767         this.update();
14768         this.place();
14769         
14770         this.fireEvent('show', this, this.date);
14771     },
14772     
14773     hide : function()
14774     {
14775         if(this.isInline) return;
14776         this.picker().hide();
14777         this.viewMode = this.startViewMode;
14778         this.showMode();
14779         
14780         this.fireEvent('hide', this, this.date);
14781         
14782     },
14783     
14784     onMousedown: function(e)
14785     {
14786         e.stopPropagation();
14787         e.preventDefault();
14788     },
14789     
14790     keyup: function(e)
14791     {
14792         Roo.bootstrap.DateField.superclass.keyup.call(this);
14793         this.update();
14794     },
14795
14796     setValue: function(v)
14797     {
14798         var d = new Date(v).clearTime();
14799         
14800         if(isNaN(d.getTime())){
14801             this.date = this.viewDate = '';
14802             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14803             return;
14804         }
14805         
14806         v = this.formatDate(d);
14807         
14808         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14809         
14810         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14811      
14812         this.update();
14813
14814         this.fireEvent('select', this, this.date);
14815         
14816     },
14817     
14818     getValue: function()
14819     {
14820         return this.formatDate(this.date);
14821     },
14822     
14823     fireKey: function(e)
14824     {
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         var dateChanged = false,
14832         dir, day, month,
14833         newDate, newViewDate;
14834         
14835         switch(e.keyCode){
14836             case 27: // escape
14837                 this.hide();
14838                 e.preventDefault();
14839                 break;
14840             case 37: // left
14841             case 39: // right
14842                 if (!this.keyboardNavigation) break;
14843                 dir = e.keyCode == 37 ? -1 : 1;
14844                 
14845                 if (e.ctrlKey){
14846                     newDate = this.moveYear(this.date, dir);
14847                     newViewDate = this.moveYear(this.viewDate, dir);
14848                 } else if (e.shiftKey){
14849                     newDate = this.moveMonth(this.date, dir);
14850                     newViewDate = this.moveMonth(this.viewDate, dir);
14851                 } else {
14852                     newDate = new Date(this.date);
14853                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14854                     newViewDate = new Date(this.viewDate);
14855                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14856                 }
14857                 if (this.dateWithinRange(newDate)){
14858                     this.date = newDate;
14859                     this.viewDate = newViewDate;
14860                     this.setValue(this.formatDate(this.date));
14861 //                    this.update();
14862                     e.preventDefault();
14863                     dateChanged = true;
14864                 }
14865                 break;
14866             case 38: // up
14867             case 40: // down
14868                 if (!this.keyboardNavigation) break;
14869                 dir = e.keyCode == 38 ? -1 : 1;
14870                 if (e.ctrlKey){
14871                     newDate = this.moveYear(this.date, dir);
14872                     newViewDate = this.moveYear(this.viewDate, dir);
14873                 } else if (e.shiftKey){
14874                     newDate = this.moveMonth(this.date, dir);
14875                     newViewDate = this.moveMonth(this.viewDate, dir);
14876                 } else {
14877                     newDate = new Date(this.date);
14878                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14879                     newViewDate = new Date(this.viewDate);
14880                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14881                 }
14882                 if (this.dateWithinRange(newDate)){
14883                     this.date = newDate;
14884                     this.viewDate = newViewDate;
14885                     this.setValue(this.formatDate(this.date));
14886 //                    this.update();
14887                     e.preventDefault();
14888                     dateChanged = true;
14889                 }
14890                 break;
14891             case 13: // enter
14892                 this.setValue(this.formatDate(this.date));
14893                 this.hide();
14894                 e.preventDefault();
14895                 break;
14896             case 9: // tab
14897                 this.setValue(this.formatDate(this.date));
14898                 this.hide();
14899                 break;
14900             case 16: // shift
14901             case 17: // ctrl
14902             case 18: // alt
14903                 break;
14904             default :
14905                 this.hide();
14906                 
14907         }
14908     },
14909     
14910     
14911     onClick: function(e) 
14912     {
14913         e.stopPropagation();
14914         e.preventDefault();
14915         
14916         var target = e.getTarget();
14917         
14918         if(target.nodeName.toLowerCase() === 'i'){
14919             target = Roo.get(target).dom.parentNode;
14920         }
14921         
14922         var nodeName = target.nodeName;
14923         var className = target.className;
14924         var html = target.innerHTML;
14925         
14926         switch(nodeName.toLowerCase()) {
14927             case 'th':
14928                 switch(className) {
14929                     case 'switch':
14930                         this.showMode(1);
14931                         break;
14932                     case 'prev':
14933                     case 'next':
14934                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14935                         switch(this.viewMode){
14936                                 case 0:
14937                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14938                                         break;
14939                                 case 1:
14940                                 case 2:
14941                                         this.viewDate = this.moveYear(this.viewDate, dir);
14942                                         break;
14943                         }
14944                         this.fill();
14945                         break;
14946                     case 'today':
14947                         var date = new Date();
14948                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14949 //                        this.fill()
14950                         this.setValue(this.formatDate(this.date));
14951                         
14952                         this.hide();
14953                         break;
14954                 }
14955                 break;
14956             case 'span':
14957                 if (className.indexOf('disabled') === -1) {
14958                     this.viewDate.setUTCDate(1);
14959                     if (className.indexOf('month') !== -1) {
14960                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14961                     } else {
14962                         var year = parseInt(html, 10) || 0;
14963                         this.viewDate.setUTCFullYear(year);
14964                         
14965                     }
14966                     this.showMode(-1);
14967                     this.fill();
14968                 }
14969                 break;
14970                 
14971             case 'td':
14972                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14973                     var day = parseInt(html, 10) || 1;
14974                     var year = this.viewDate.getUTCFullYear(),
14975                         month = this.viewDate.getUTCMonth();
14976
14977                     if (className.indexOf('old') !== -1) {
14978                         if(month === 0 ){
14979                             month = 11;
14980                             year -= 1;
14981                         }else{
14982                             month -= 1;
14983                         }
14984                     } else if (className.indexOf('new') !== -1) {
14985                         if (month == 11) {
14986                             month = 0;
14987                             year += 1;
14988                         } else {
14989                             month += 1;
14990                         }
14991                     }
14992                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14993                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14994 //                    this.fill();
14995                     this.setValue(this.formatDate(this.date));
14996                     this.hide();
14997                 }
14998                 break;
14999         }
15000     },
15001     
15002     setStartDate: function(startDate)
15003     {
15004         this.startDate = startDate || -Infinity;
15005         if (this.startDate !== -Infinity) {
15006             this.startDate = this.parseDate(this.startDate);
15007         }
15008         this.update();
15009         this.updateNavArrows();
15010     },
15011
15012     setEndDate: function(endDate)
15013     {
15014         this.endDate = endDate || Infinity;
15015         if (this.endDate !== Infinity) {
15016             this.endDate = this.parseDate(this.endDate);
15017         }
15018         this.update();
15019         this.updateNavArrows();
15020     },
15021     
15022     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15023     {
15024         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15025         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15026             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15027         }
15028         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15029             return parseInt(d, 10);
15030         });
15031         this.update();
15032         this.updateNavArrows();
15033     },
15034     
15035     updateNavArrows: function() 
15036     {
15037         var d = new Date(this.viewDate),
15038         year = d.getUTCFullYear(),
15039         month = d.getUTCMonth();
15040         
15041         Roo.each(this.picker().select('.prev', true).elements, function(v){
15042             v.show();
15043             switch (this.viewMode) {
15044                 case 0:
15045
15046                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15047                         v.hide();
15048                     }
15049                     break;
15050                 case 1:
15051                 case 2:
15052                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15053                         v.hide();
15054                     }
15055                     break;
15056             }
15057         });
15058         
15059         Roo.each(this.picker().select('.next', true).elements, function(v){
15060             v.show();
15061             switch (this.viewMode) {
15062                 case 0:
15063
15064                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15065                         v.hide();
15066                     }
15067                     break;
15068                 case 1:
15069                 case 2:
15070                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15071                         v.hide();
15072                     }
15073                     break;
15074             }
15075         })
15076     },
15077     
15078     moveMonth: function(date, dir)
15079     {
15080         if (!dir) return date;
15081         var new_date = new Date(date.valueOf()),
15082         day = new_date.getUTCDate(),
15083         month = new_date.getUTCMonth(),
15084         mag = Math.abs(dir),
15085         new_month, test;
15086         dir = dir > 0 ? 1 : -1;
15087         if (mag == 1){
15088             test = dir == -1
15089             // If going back one month, make sure month is not current month
15090             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15091             ? function(){
15092                 return new_date.getUTCMonth() == month;
15093             }
15094             // If going forward one month, make sure month is as expected
15095             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15096             : function(){
15097                 return new_date.getUTCMonth() != new_month;
15098             };
15099             new_month = month + dir;
15100             new_date.setUTCMonth(new_month);
15101             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15102             if (new_month < 0 || new_month > 11)
15103                 new_month = (new_month + 12) % 12;
15104         } else {
15105             // For magnitudes >1, move one month at a time...
15106             for (var i=0; i<mag; i++)
15107                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15108                 new_date = this.moveMonth(new_date, dir);
15109             // ...then reset the day, keeping it in the new month
15110             new_month = new_date.getUTCMonth();
15111             new_date.setUTCDate(day);
15112             test = function(){
15113                 return new_month != new_date.getUTCMonth();
15114             };
15115         }
15116         // Common date-resetting loop -- if date is beyond end of month, make it
15117         // end of month
15118         while (test()){
15119             new_date.setUTCDate(--day);
15120             new_date.setUTCMonth(new_month);
15121         }
15122         return new_date;
15123     },
15124
15125     moveYear: function(date, dir)
15126     {
15127         return this.moveMonth(date, dir*12);
15128     },
15129
15130     dateWithinRange: function(date)
15131     {
15132         return date >= this.startDate && date <= this.endDate;
15133     },
15134
15135     
15136     remove: function() 
15137     {
15138         this.picker().remove();
15139     }
15140    
15141 });
15142
15143 Roo.apply(Roo.bootstrap.DateField,  {
15144     
15145     head : {
15146         tag: 'thead',
15147         cn: [
15148         {
15149             tag: 'tr',
15150             cn: [
15151             {
15152                 tag: 'th',
15153                 cls: 'prev',
15154                 html: '<i class="fa fa-arrow-left"/>'
15155             },
15156             {
15157                 tag: 'th',
15158                 cls: 'switch',
15159                 colspan: '5'
15160             },
15161             {
15162                 tag: 'th',
15163                 cls: 'next',
15164                 html: '<i class="fa fa-arrow-right"/>'
15165             }
15166
15167             ]
15168         }
15169         ]
15170     },
15171     
15172     content : {
15173         tag: 'tbody',
15174         cn: [
15175         {
15176             tag: 'tr',
15177             cn: [
15178             {
15179                 tag: 'td',
15180                 colspan: '7'
15181             }
15182             ]
15183         }
15184         ]
15185     },
15186     
15187     footer : {
15188         tag: 'tfoot',
15189         cn: [
15190         {
15191             tag: 'tr',
15192             cn: [
15193             {
15194                 tag: 'th',
15195                 colspan: '7',
15196                 cls: 'today'
15197             }
15198                     
15199             ]
15200         }
15201         ]
15202     },
15203     
15204     dates:{
15205         en: {
15206             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15207             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15208             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15209             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15210             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15211             today: "Today"
15212         }
15213     },
15214     
15215     modes: [
15216     {
15217         clsName: 'days',
15218         navFnc: 'Month',
15219         navStep: 1
15220     },
15221     {
15222         clsName: 'months',
15223         navFnc: 'FullYear',
15224         navStep: 1
15225     },
15226     {
15227         clsName: 'years',
15228         navFnc: 'FullYear',
15229         navStep: 10
15230     }]
15231 });
15232
15233 Roo.apply(Roo.bootstrap.DateField,  {
15234   
15235     template : {
15236         tag: 'div',
15237         cls: 'datepicker dropdown-menu',
15238         cn: [
15239         {
15240             tag: 'div',
15241             cls: 'datepicker-days',
15242             cn: [
15243             {
15244                 tag: 'table',
15245                 cls: 'table-condensed',
15246                 cn:[
15247                 Roo.bootstrap.DateField.head,
15248                 {
15249                     tag: 'tbody'
15250                 },
15251                 Roo.bootstrap.DateField.footer
15252                 ]
15253             }
15254             ]
15255         },
15256         {
15257             tag: 'div',
15258             cls: 'datepicker-months',
15259             cn: [
15260             {
15261                 tag: 'table',
15262                 cls: 'table-condensed',
15263                 cn:[
15264                 Roo.bootstrap.DateField.head,
15265                 Roo.bootstrap.DateField.content,
15266                 Roo.bootstrap.DateField.footer
15267                 ]
15268             }
15269             ]
15270         },
15271         {
15272             tag: 'div',
15273             cls: 'datepicker-years',
15274             cn: [
15275             {
15276                 tag: 'table',
15277                 cls: 'table-condensed',
15278                 cn:[
15279                 Roo.bootstrap.DateField.head,
15280                 Roo.bootstrap.DateField.content,
15281                 Roo.bootstrap.DateField.footer
15282                 ]
15283             }
15284             ]
15285         }
15286         ]
15287     }
15288 });
15289
15290  
15291
15292  /*
15293  * - LGPL
15294  *
15295  * TimeField
15296  * 
15297  */
15298
15299 /**
15300  * @class Roo.bootstrap.TimeField
15301  * @extends Roo.bootstrap.Input
15302  * Bootstrap DateField class
15303  * 
15304  * 
15305  * @constructor
15306  * Create a new TimeField
15307  * @param {Object} config The config object
15308  */
15309
15310 Roo.bootstrap.TimeField = function(config){
15311     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15312     this.addEvents({
15313             /**
15314              * @event show
15315              * Fires when this field show.
15316              * @param {Roo.bootstrap.DateField} this
15317              * @param {Mixed} date The date value
15318              */
15319             show : true,
15320             /**
15321              * @event show
15322              * Fires when this field hide.
15323              * @param {Roo.bootstrap.DateField} this
15324              * @param {Mixed} date The date value
15325              */
15326             hide : true,
15327             /**
15328              * @event select
15329              * Fires when select a date.
15330              * @param {Roo.bootstrap.DateField} this
15331              * @param {Mixed} date The date value
15332              */
15333             select : true
15334         });
15335 };
15336
15337 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15338     
15339     /**
15340      * @cfg {String} format
15341      * The default time format string which can be overriden for localization support.  The format must be
15342      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15343      */
15344     format : "H:i",
15345        
15346     onRender: function(ct, position)
15347     {
15348         
15349         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15350                 
15351         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15352         
15353         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15354         
15355         this.pop = this.picker().select('>.datepicker-time',true).first();
15356         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15357         
15358         this.picker().on('mousedown', this.onMousedown, this);
15359         this.picker().on('click', this.onClick, this);
15360         
15361         this.picker().addClass('datepicker-dropdown');
15362     
15363         this.fillTime();
15364         this.update();
15365             
15366         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15367         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15368         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15369         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15370         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15371         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15372
15373     },
15374     
15375     fireKey: function(e){
15376         if (!this.picker().isVisible()){
15377             if (e.keyCode == 27) // allow escape to hide and re-show picker
15378                 this.show();
15379             return;
15380         }
15381
15382         e.preventDefault();
15383         
15384         switch(e.keyCode){
15385             case 27: // escape
15386                 this.hide();
15387                 break;
15388             case 37: // left
15389             case 39: // right
15390                 this.onTogglePeriod();
15391                 break;
15392             case 38: // up
15393                 this.onIncrementMinutes();
15394                 break;
15395             case 40: // down
15396                 this.onDecrementMinutes();
15397                 break;
15398             case 13: // enter
15399             case 9: // tab
15400                 this.setTime();
15401                 break;
15402         }
15403     },
15404     
15405     onClick: function(e) {
15406         e.stopPropagation();
15407         e.preventDefault();
15408     },
15409     
15410     picker : function()
15411     {
15412         return this.el.select('.datepicker', true).first();
15413     },
15414     
15415     fillTime: function()
15416     {    
15417         var time = this.pop.select('tbody', true).first();
15418         
15419         time.dom.innerHTML = '';
15420         
15421         time.createChild({
15422             tag: 'tr',
15423             cn: [
15424                 {
15425                     tag: 'td',
15426                     cn: [
15427                         {
15428                             tag: 'a',
15429                             href: '#',
15430                             cls: 'btn',
15431                             cn: [
15432                                 {
15433                                     tag: 'span',
15434                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15435                                 }
15436                             ]
15437                         } 
15438                     ]
15439                 },
15440                 {
15441                     tag: 'td',
15442                     cls: 'separator'
15443                 },
15444                 {
15445                     tag: 'td',
15446                     cn: [
15447                         {
15448                             tag: 'a',
15449                             href: '#',
15450                             cls: 'btn',
15451                             cn: [
15452                                 {
15453                                     tag: 'span',
15454                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15455                                 }
15456                             ]
15457                         }
15458                     ]
15459                 },
15460                 {
15461                     tag: 'td',
15462                     cls: 'separator'
15463                 }
15464             ]
15465         });
15466         
15467         time.createChild({
15468             tag: 'tr',
15469             cn: [
15470                 {
15471                     tag: 'td',
15472                     cn: [
15473                         {
15474                             tag: 'span',
15475                             cls: 'timepicker-hour',
15476                             html: '00'
15477                         }  
15478                     ]
15479                 },
15480                 {
15481                     tag: 'td',
15482                     cls: 'separator',
15483                     html: ':'
15484                 },
15485                 {
15486                     tag: 'td',
15487                     cn: [
15488                         {
15489                             tag: 'span',
15490                             cls: 'timepicker-minute',
15491                             html: '00'
15492                         }  
15493                     ]
15494                 },
15495                 {
15496                     tag: 'td',
15497                     cls: 'separator'
15498                 },
15499                 {
15500                     tag: 'td',
15501                     cn: [
15502                         {
15503                             tag: 'button',
15504                             type: 'button',
15505                             cls: 'btn btn-primary period',
15506                             html: 'AM'
15507                             
15508                         }
15509                     ]
15510                 }
15511             ]
15512         });
15513         
15514         time.createChild({
15515             tag: 'tr',
15516             cn: [
15517                 {
15518                     tag: 'td',
15519                     cn: [
15520                         {
15521                             tag: 'a',
15522                             href: '#',
15523                             cls: 'btn',
15524                             cn: [
15525                                 {
15526                                     tag: 'span',
15527                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15528                                 }
15529                             ]
15530                         }
15531                     ]
15532                 },
15533                 {
15534                     tag: 'td',
15535                     cls: 'separator'
15536                 },
15537                 {
15538                     tag: 'td',
15539                     cn: [
15540                         {
15541                             tag: 'a',
15542                             href: '#',
15543                             cls: 'btn',
15544                             cn: [
15545                                 {
15546                                     tag: 'span',
15547                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15548                                 }
15549                             ]
15550                         }
15551                     ]
15552                 },
15553                 {
15554                     tag: 'td',
15555                     cls: 'separator'
15556                 }
15557             ]
15558         });
15559         
15560     },
15561     
15562     update: function()
15563     {
15564         
15565         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15566         
15567         this.fill();
15568     },
15569     
15570     fill: function() 
15571     {
15572         var hours = this.time.getHours();
15573         var minutes = this.time.getMinutes();
15574         var period = 'AM';
15575         
15576         if(hours > 11){
15577             period = 'PM';
15578         }
15579         
15580         if(hours == 0){
15581             hours = 12;
15582         }
15583         
15584         
15585         if(hours > 12){
15586             hours = hours - 12;
15587         }
15588         
15589         if(hours < 10){
15590             hours = '0' + hours;
15591         }
15592         
15593         if(minutes < 10){
15594             minutes = '0' + minutes;
15595         }
15596         
15597         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15598         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15599         this.pop.select('button', true).first().dom.innerHTML = period;
15600         
15601     },
15602     
15603     place: function()
15604     {   
15605         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15606         
15607         var cls = ['bottom'];
15608         
15609         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15610             cls.pop();
15611             cls.push('top');
15612         }
15613         
15614         cls.push('right');
15615         
15616         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15617             cls.pop();
15618             cls.push('left');
15619         }
15620         
15621         this.picker().addClass(cls.join('-'));
15622         
15623         var _this = this;
15624         
15625         Roo.each(cls, function(c){
15626             if(c == 'bottom'){
15627                 _this.picker().setTop(_this.inputEl().getHeight());
15628                 return;
15629             }
15630             if(c == 'top'){
15631                 _this.picker().setTop(0 - _this.picker().getHeight());
15632                 return;
15633             }
15634             
15635             if(c == 'left'){
15636                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15637                 return;
15638             }
15639             if(c == 'right'){
15640                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15641                 return;
15642             }
15643         });
15644         
15645     },
15646   
15647     onFocus : function()
15648     {
15649         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15650         this.show();
15651     },
15652     
15653     onBlur : function()
15654     {
15655         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15656         this.hide();
15657     },
15658     
15659     show : function()
15660     {
15661         this.picker().show();
15662         this.pop.show();
15663         this.update();
15664         this.place();
15665         
15666         this.fireEvent('show', this, this.date);
15667     },
15668     
15669     hide : function()
15670     {
15671         this.picker().hide();
15672         this.pop.hide();
15673         
15674         this.fireEvent('hide', this, this.date);
15675     },
15676     
15677     setTime : function()
15678     {
15679         this.hide();
15680         this.setValue(this.time.format(this.format));
15681         
15682         this.fireEvent('select', this, this.date);
15683         
15684         
15685     },
15686     
15687     onMousedown: function(e){
15688         e.stopPropagation();
15689         e.preventDefault();
15690     },
15691     
15692     onIncrementHours: function()
15693     {
15694         Roo.log('onIncrementHours');
15695         this.time = this.time.add(Date.HOUR, 1);
15696         this.update();
15697         
15698     },
15699     
15700     onDecrementHours: function()
15701     {
15702         Roo.log('onDecrementHours');
15703         this.time = this.time.add(Date.HOUR, -1);
15704         this.update();
15705     },
15706     
15707     onIncrementMinutes: function()
15708     {
15709         Roo.log('onIncrementMinutes');
15710         this.time = this.time.add(Date.MINUTE, 1);
15711         this.update();
15712     },
15713     
15714     onDecrementMinutes: function()
15715     {
15716         Roo.log('onDecrementMinutes');
15717         this.time = this.time.add(Date.MINUTE, -1);
15718         this.update();
15719     },
15720     
15721     onTogglePeriod: function()
15722     {
15723         Roo.log('onTogglePeriod');
15724         this.time = this.time.add(Date.HOUR, 12);
15725         this.update();
15726     }
15727     
15728    
15729 });
15730
15731 Roo.apply(Roo.bootstrap.TimeField,  {
15732     
15733     content : {
15734         tag: 'tbody',
15735         cn: [
15736             {
15737                 tag: 'tr',
15738                 cn: [
15739                 {
15740                     tag: 'td',
15741                     colspan: '7'
15742                 }
15743                 ]
15744             }
15745         ]
15746     },
15747     
15748     footer : {
15749         tag: 'tfoot',
15750         cn: [
15751             {
15752                 tag: 'tr',
15753                 cn: [
15754                 {
15755                     tag: 'th',
15756                     colspan: '7',
15757                     cls: '',
15758                     cn: [
15759                         {
15760                             tag: 'button',
15761                             cls: 'btn btn-info ok',
15762                             html: 'OK'
15763                         }
15764                     ]
15765                 }
15766
15767                 ]
15768             }
15769         ]
15770     }
15771 });
15772
15773 Roo.apply(Roo.bootstrap.TimeField,  {
15774   
15775     template : {
15776         tag: 'div',
15777         cls: 'datepicker dropdown-menu',
15778         cn: [
15779             {
15780                 tag: 'div',
15781                 cls: 'datepicker-time',
15782                 cn: [
15783                 {
15784                     tag: 'table',
15785                     cls: 'table-condensed',
15786                     cn:[
15787                     Roo.bootstrap.TimeField.content,
15788                     Roo.bootstrap.TimeField.footer
15789                     ]
15790                 }
15791                 ]
15792             }
15793         ]
15794     }
15795 });
15796
15797  
15798
15799  /*
15800  * - LGPL
15801  *
15802  * CheckBox
15803  * 
15804  */
15805
15806 /**
15807  * @class Roo.bootstrap.CheckBox
15808  * @extends Roo.bootstrap.Input
15809  * Bootstrap CheckBox class
15810  * 
15811  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15812  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15813  * @cfg {String} boxLabel The text that appears beside the checkbox
15814  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15815  * @cfg {Boolean} checked initnal the element
15816  * 
15817  * 
15818  * @constructor
15819  * Create a new CheckBox
15820  * @param {Object} config The config object
15821  */
15822
15823 Roo.bootstrap.CheckBox = function(config){
15824     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15825    
15826         this.addEvents({
15827             /**
15828             * @event check
15829             * Fires when the element is checked or unchecked.
15830             * @param {Roo.bootstrap.CheckBox} this This input
15831             * @param {Boolean} checked The new checked value
15832             */
15833            check : true
15834         });
15835 };
15836
15837 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15838     
15839     inputType: 'checkbox',
15840     inputValue: 1,
15841     valueOff: 0,
15842     boxLabel: false,
15843     checked: false,
15844     weight : false,
15845     
15846     getAutoCreate : function()
15847     {
15848         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15849         
15850         var id = Roo.id();
15851         
15852         var cfg = {};
15853         
15854         cfg.cls = 'form-group checkbox' //input-group
15855         
15856         
15857         
15858         
15859         var input =  {
15860             tag: 'input',
15861             id : id,
15862             type : this.inputType,
15863             value : (!this.checked) ? this.valueOff : this.inputValue,
15864             cls : 'roo-checkbox', //'form-box',
15865             placeholder : this.placeholder || ''
15866             
15867         };
15868         
15869         if (this.weight) { // Validity check?
15870             cfg.cls += " checkbox-" + this.weight;
15871         }
15872         
15873         if (this.disabled) {
15874             input.disabled=true;
15875         }
15876         
15877         if(this.checked){
15878             input.checked = this.checked;
15879         }
15880         
15881         if (this.name) {
15882             input.name = this.name;
15883         }
15884         
15885         if (this.size) {
15886             input.cls += ' input-' + this.size;
15887         }
15888         
15889         var settings=this;
15890         ['xs','sm','md','lg'].map(function(size){
15891             if (settings[size]) {
15892                 cfg.cls += ' col-' + size + '-' + settings[size];
15893             }
15894         });
15895         
15896        
15897         
15898         var inputblock = input;
15899         
15900         
15901         
15902         
15903         if (this.before || this.after) {
15904             
15905             inputblock = {
15906                 cls : 'input-group',
15907                 cn :  [] 
15908             };
15909             if (this.before) {
15910                 inputblock.cn.push({
15911                     tag :'span',
15912                     cls : 'input-group-addon',
15913                     html : this.before
15914                 });
15915             }
15916             inputblock.cn.push(input);
15917             if (this.after) {
15918                 inputblock.cn.push({
15919                     tag :'span',
15920                     cls : 'input-group-addon',
15921                     html : this.after
15922                 });
15923             }
15924             
15925         };
15926         
15927         if (align ==='left' && this.fieldLabel.length) {
15928                 Roo.log("left and has label");
15929                 cfg.cn = [
15930                     
15931                     {
15932                         tag: 'label',
15933                         'for' :  id,
15934                         cls : 'control-label col-md-' + this.labelWidth,
15935                         html : this.fieldLabel
15936                         
15937                     },
15938                     {
15939                         cls : "col-md-" + (12 - this.labelWidth), 
15940                         cn: [
15941                             inputblock
15942                         ]
15943                     }
15944                     
15945                 ];
15946         } else if ( this.fieldLabel.length) {
15947                 Roo.log(" label");
15948                 cfg.cn = [
15949                    
15950                     {
15951                         tag: this.boxLabel ? 'span' : 'label',
15952                         'for': id,
15953                         cls: 'control-label box-input-label',
15954                         //cls : 'input-group-addon',
15955                         html : this.fieldLabel
15956                         
15957                     },
15958                     
15959                     inputblock
15960                     
15961                 ];
15962
15963         } else {
15964             
15965                 Roo.log(" no label && no align");
15966                 cfg.cn = [  inputblock ] ;
15967                 
15968                 
15969         };
15970          if(this.boxLabel){
15971             cfg.cn.push( {
15972                 tag: 'label',
15973                 'for': id,
15974                 cls: 'box-label',
15975                 html: this.boxLabel
15976                 
15977             });
15978         }
15979         
15980         
15981        
15982         return cfg;
15983         
15984     },
15985     
15986     /**
15987      * return the real input element.
15988      */
15989     inputEl: function ()
15990     {
15991         return this.el.select('input.roo-checkbox',true).first();
15992     },
15993     
15994     label: function()
15995     {
15996         return this.el.select('label.control-label',true).first();
15997     },
15998     
15999     initEvents : function()
16000     {
16001 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16002         
16003         this.inputEl().on('click', this.onClick,  this);
16004         
16005     },
16006     
16007     onClick : function()
16008     {   
16009         this.setChecked(!this.checked);
16010     },
16011     
16012     setChecked : function(state,suppressEvent)
16013     {
16014         this.checked = state;
16015         
16016         this.inputEl().dom.checked = state;
16017         
16018         if(suppressEvent !== true){
16019             this.fireEvent('check', this, state);
16020         }
16021         
16022         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16023         
16024     },
16025     
16026     setValue : function(v,suppressEvent)
16027     {
16028         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16029     }
16030     
16031 });
16032
16033  
16034 /*
16035  * - LGPL
16036  *
16037  * Radio
16038  * 
16039  */
16040
16041 /**
16042  * @class Roo.bootstrap.Radio
16043  * @extends Roo.bootstrap.CheckBox
16044  * Bootstrap Radio class
16045
16046  * @constructor
16047  * Create a new Radio
16048  * @param {Object} config The config object
16049  */
16050
16051 Roo.bootstrap.Radio = function(config){
16052     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16053    
16054 };
16055
16056 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16057     
16058     inputType: 'radio',
16059     inputValue: '',
16060     valueOff: '',
16061     
16062     getAutoCreate : function()
16063     {
16064         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16065         
16066         var id = Roo.id();
16067         
16068         var cfg = {};
16069         
16070         cfg.cls = 'form-group radio' //input-group
16071         
16072         var input =  {
16073             tag: 'input',
16074             id : id,
16075             type : this.inputType,
16076             value : (!this.checked) ? this.valueOff : this.inputValue,
16077             cls : 'roo-radio',
16078             placeholder : this.placeholder || ''
16079             
16080         };
16081           if (this.weight) { // Validity check?
16082             cfg.cls += " radio-" + this.weight;
16083         }
16084         if (this.disabled) {
16085             input.disabled=true;
16086         }
16087         
16088         if(this.checked){
16089             input.checked = this.checked;
16090         }
16091         
16092         if (this.name) {
16093             input.name = this.name;
16094         }
16095         
16096         if (this.size) {
16097             input.cls += ' input-' + this.size;
16098         }
16099         
16100         var settings=this;
16101         ['xs','sm','md','lg'].map(function(size){
16102             if (settings[size]) {
16103                 cfg.cls += ' col-' + size + '-' + settings[size];
16104             }
16105         });
16106         
16107         var inputblock = input;
16108         
16109         if (this.before || this.after) {
16110             
16111             inputblock = {
16112                 cls : 'input-group',
16113                 cn :  [] 
16114             };
16115             if (this.before) {
16116                 inputblock.cn.push({
16117                     tag :'span',
16118                     cls : 'input-group-addon',
16119                     html : this.before
16120                 });
16121             }
16122             inputblock.cn.push(input);
16123             if (this.after) {
16124                 inputblock.cn.push({
16125                     tag :'span',
16126                     cls : 'input-group-addon',
16127                     html : this.after
16128                 });
16129             }
16130             
16131         };
16132         
16133         if (align ==='left' && this.fieldLabel.length) {
16134                 Roo.log("left and has label");
16135                 cfg.cn = [
16136                     
16137                     {
16138                         tag: 'label',
16139                         'for' :  id,
16140                         cls : 'control-label col-md-' + this.labelWidth,
16141                         html : this.fieldLabel
16142                         
16143                     },
16144                     {
16145                         cls : "col-md-" + (12 - this.labelWidth), 
16146                         cn: [
16147                             inputblock
16148                         ]
16149                     }
16150                     
16151                 ];
16152         } else if ( this.fieldLabel.length) {
16153                 Roo.log(" label");
16154                  cfg.cn = [
16155                    
16156                     {
16157                         tag: 'label',
16158                         'for': id,
16159                         cls: 'control-label box-input-label',
16160                         //cls : 'input-group-addon',
16161                         html : this.fieldLabel
16162                         
16163                     },
16164                     
16165                     inputblock
16166                     
16167                 ];
16168
16169         } else {
16170             
16171                    Roo.log(" no label && no align");
16172                 cfg.cn = [
16173                     
16174                         inputblock
16175                     
16176                 ];
16177                 
16178                 
16179         };
16180         
16181         if(this.boxLabel){
16182             cfg.cn.push({
16183                 tag: 'label',
16184                 'for': id,
16185                 cls: 'box-label',
16186                 html: this.boxLabel
16187             })
16188         }
16189         
16190         return cfg;
16191         
16192     },
16193     inputEl: function ()
16194     {
16195         return this.el.select('input.roo-radio',true).first();
16196     },
16197     onClick : function()
16198     {   
16199         this.setChecked(true);
16200     },
16201     
16202     setChecked : function(state,suppressEvent)
16203     {
16204         if(state){
16205             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16206                 v.dom.checked = false;
16207             });
16208         }
16209         
16210         this.checked = state;
16211         this.inputEl().dom.checked = state;
16212         
16213         if(suppressEvent !== true){
16214             this.fireEvent('check', this, state);
16215         }
16216         
16217         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16218         
16219     },
16220     
16221     getGroupValue : function()
16222     {
16223         var value = ''
16224         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16225             if(v.dom.checked == true){
16226                 value = v.dom.value;
16227             }
16228         });
16229         
16230         return value;
16231     },
16232     
16233     /**
16234      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16235      * @return {Mixed} value The field value
16236      */
16237     getValue : function(){
16238         return this.getGroupValue();
16239     }
16240     
16241 });
16242
16243  
16244 //<script type="text/javascript">
16245
16246 /*
16247  * Based  Ext JS Library 1.1.1
16248  * Copyright(c) 2006-2007, Ext JS, LLC.
16249  * LGPL
16250  *
16251  */
16252  
16253 /**
16254  * @class Roo.HtmlEditorCore
16255  * @extends Roo.Component
16256  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16257  *
16258  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16259  */
16260
16261 Roo.HtmlEditorCore = function(config){
16262     
16263     
16264     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16265     this.addEvents({
16266         /**
16267          * @event initialize
16268          * Fires when the editor is fully initialized (including the iframe)
16269          * @param {Roo.HtmlEditorCore} this
16270          */
16271         initialize: true,
16272         /**
16273          * @event activate
16274          * Fires when the editor is first receives the focus. Any insertion must wait
16275          * until after this event.
16276          * @param {Roo.HtmlEditorCore} this
16277          */
16278         activate: true,
16279          /**
16280          * @event beforesync
16281          * Fires before the textarea is updated with content from the editor iframe. Return false
16282          * to cancel the sync.
16283          * @param {Roo.HtmlEditorCore} this
16284          * @param {String} html
16285          */
16286         beforesync: true,
16287          /**
16288          * @event beforepush
16289          * Fires before the iframe editor is updated with content from the textarea. Return false
16290          * to cancel the push.
16291          * @param {Roo.HtmlEditorCore} this
16292          * @param {String} html
16293          */
16294         beforepush: true,
16295          /**
16296          * @event sync
16297          * Fires when the textarea is updated with content from the editor iframe.
16298          * @param {Roo.HtmlEditorCore} this
16299          * @param {String} html
16300          */
16301         sync: true,
16302          /**
16303          * @event push
16304          * Fires when the iframe editor is updated with content from the textarea.
16305          * @param {Roo.HtmlEditorCore} this
16306          * @param {String} html
16307          */
16308         push: true,
16309         
16310         /**
16311          * @event editorevent
16312          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16313          * @param {Roo.HtmlEditorCore} this
16314          */
16315         editorevent: true
16316     });
16317      
16318 };
16319
16320
16321 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16322
16323
16324      /**
16325      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16326      */
16327     
16328     owner : false,
16329     
16330      /**
16331      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16332      *                        Roo.resizable.
16333      */
16334     resizable : false,
16335      /**
16336      * @cfg {Number} height (in pixels)
16337      */   
16338     height: 300,
16339    /**
16340      * @cfg {Number} width (in pixels)
16341      */   
16342     width: 500,
16343     
16344     /**
16345      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16346      * 
16347      */
16348     stylesheets: false,
16349     
16350     // id of frame..
16351     frameId: false,
16352     
16353     // private properties
16354     validationEvent : false,
16355     deferHeight: true,
16356     initialized : false,
16357     activated : false,
16358     sourceEditMode : false,
16359     onFocus : Roo.emptyFn,
16360     iframePad:3,
16361     hideMode:'offsets',
16362     
16363     clearUp: true,
16364     
16365      
16366     
16367
16368     /**
16369      * Protected method that will not generally be called directly. It
16370      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16371      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16372      */
16373     getDocMarkup : function(){
16374         // body styles..
16375         var st = '';
16376         Roo.log(this.stylesheets);
16377         
16378         // inherit styels from page...?? 
16379         if (this.stylesheets === false) {
16380             
16381             Roo.get(document.head).select('style').each(function(node) {
16382                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16383             });
16384             
16385             Roo.get(document.head).select('link').each(function(node) { 
16386                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16387             });
16388             
16389         } else if (!this.stylesheets.length) {
16390                 // simple..
16391                 st = '<style type="text/css">' +
16392                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16393                    '</style>';
16394         } else {
16395             Roo.each(this.stylesheets, function(s) {
16396                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16397             });
16398             
16399         }
16400         
16401         st +=  '<style type="text/css">' +
16402             'IMG { cursor: pointer } ' +
16403         '</style>';
16404
16405         
16406         return '<html><head>' + st  +
16407             //<style type="text/css">' +
16408             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16409             //'</style>' +
16410             ' </head><body class="roo-htmleditor-body"></body></html>';
16411     },
16412
16413     // private
16414     onRender : function(ct, position)
16415     {
16416         var _t = this;
16417         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16418         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16419         
16420         
16421         this.el.dom.style.border = '0 none';
16422         this.el.dom.setAttribute('tabIndex', -1);
16423         this.el.addClass('x-hidden hide');
16424         
16425         
16426         
16427         if(Roo.isIE){ // fix IE 1px bogus margin
16428             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16429         }
16430        
16431         
16432         this.frameId = Roo.id();
16433         
16434          
16435         
16436         var iframe = this.owner.wrap.createChild({
16437             tag: 'iframe',
16438             cls: 'form-control', // bootstrap..
16439             id: this.frameId,
16440             name: this.frameId,
16441             frameBorder : 'no',
16442             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16443         }, this.el
16444         );
16445         
16446         
16447         this.iframe = iframe.dom;
16448
16449          this.assignDocWin();
16450         
16451         this.doc.designMode = 'on';
16452        
16453         this.doc.open();
16454         this.doc.write(this.getDocMarkup());
16455         this.doc.close();
16456
16457         
16458         var task = { // must defer to wait for browser to be ready
16459             run : function(){
16460                 //console.log("run task?" + this.doc.readyState);
16461                 this.assignDocWin();
16462                 if(this.doc.body || this.doc.readyState == 'complete'){
16463                     try {
16464                         this.doc.designMode="on";
16465                     } catch (e) {
16466                         return;
16467                     }
16468                     Roo.TaskMgr.stop(task);
16469                     this.initEditor.defer(10, this);
16470                 }
16471             },
16472             interval : 10,
16473             duration: 10000,
16474             scope: this
16475         };
16476         Roo.TaskMgr.start(task);
16477
16478         
16479          
16480     },
16481
16482     // private
16483     onResize : function(w, h)
16484     {
16485          Roo.log('resize: ' +w + ',' + h );
16486         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16487         if(!this.iframe){
16488             return;
16489         }
16490         if(typeof w == 'number'){
16491             
16492             this.iframe.style.width = w + 'px';
16493         }
16494         if(typeof h == 'number'){
16495             
16496             this.iframe.style.height = h + 'px';
16497             if(this.doc){
16498                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16499             }
16500         }
16501         
16502     },
16503
16504     /**
16505      * Toggles the editor between standard and source edit mode.
16506      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16507      */
16508     toggleSourceEdit : function(sourceEditMode){
16509         
16510         this.sourceEditMode = sourceEditMode === true;
16511         
16512         if(this.sourceEditMode){
16513  
16514             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16515             
16516         }else{
16517             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16518             //this.iframe.className = '';
16519             this.deferFocus();
16520         }
16521         //this.setSize(this.owner.wrap.getSize());
16522         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16523     },
16524
16525     
16526   
16527
16528     /**
16529      * Protected method that will not generally be called directly. If you need/want
16530      * custom HTML cleanup, this is the method you should override.
16531      * @param {String} html The HTML to be cleaned
16532      * return {String} The cleaned HTML
16533      */
16534     cleanHtml : function(html){
16535         html = String(html);
16536         if(html.length > 5){
16537             if(Roo.isSafari){ // strip safari nonsense
16538                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16539             }
16540         }
16541         if(html == '&nbsp;'){
16542             html = '';
16543         }
16544         return html;
16545     },
16546
16547     /**
16548      * HTML Editor -> Textarea
16549      * Protected method that will not generally be called directly. Syncs the contents
16550      * of the editor iframe with the textarea.
16551      */
16552     syncValue : function(){
16553         if(this.initialized){
16554             var bd = (this.doc.body || this.doc.documentElement);
16555             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16556             var html = bd.innerHTML;
16557             if(Roo.isSafari){
16558                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16559                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16560                 if(m && m[1]){
16561                     html = '<div style="'+m[0]+'">' + html + '</div>';
16562                 }
16563             }
16564             html = this.cleanHtml(html);
16565             // fix up the special chars.. normaly like back quotes in word...
16566             // however we do not want to do this with chinese..
16567             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16568                 var cc = b.charCodeAt();
16569                 if (
16570                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16571                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16572                     (cc >= 0xf900 && cc < 0xfb00 )
16573                 ) {
16574                         return b;
16575                 }
16576                 return "&#"+cc+";" 
16577             });
16578             if(this.owner.fireEvent('beforesync', this, html) !== false){
16579                 this.el.dom.value = html;
16580                 this.owner.fireEvent('sync', this, html);
16581             }
16582         }
16583     },
16584
16585     /**
16586      * Protected method that will not generally be called directly. Pushes the value of the textarea
16587      * into the iframe editor.
16588      */
16589     pushValue : function(){
16590         if(this.initialized){
16591             var v = this.el.dom.value.trim();
16592             
16593 //            if(v.length < 1){
16594 //                v = '&#160;';
16595 //            }
16596             
16597             if(this.owner.fireEvent('beforepush', this, v) !== false){
16598                 var d = (this.doc.body || this.doc.documentElement);
16599                 d.innerHTML = v;
16600                 this.cleanUpPaste();
16601                 this.el.dom.value = d.innerHTML;
16602                 this.owner.fireEvent('push', this, v);
16603             }
16604         }
16605     },
16606
16607     // private
16608     deferFocus : function(){
16609         this.focus.defer(10, this);
16610     },
16611
16612     // doc'ed in Field
16613     focus : function(){
16614         if(this.win && !this.sourceEditMode){
16615             this.win.focus();
16616         }else{
16617             this.el.focus();
16618         }
16619     },
16620     
16621     assignDocWin: function()
16622     {
16623         var iframe = this.iframe;
16624         
16625          if(Roo.isIE){
16626             this.doc = iframe.contentWindow.document;
16627             this.win = iframe.contentWindow;
16628         } else {
16629 //            if (!Roo.get(this.frameId)) {
16630 //                return;
16631 //            }
16632 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16633 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16634             
16635             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16636                 return;
16637             }
16638             
16639             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16640             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16641         }
16642     },
16643     
16644     // private
16645     initEditor : function(){
16646         //console.log("INIT EDITOR");
16647         this.assignDocWin();
16648         
16649         
16650         
16651         this.doc.designMode="on";
16652         this.doc.open();
16653         this.doc.write(this.getDocMarkup());
16654         this.doc.close();
16655         
16656         var dbody = (this.doc.body || this.doc.documentElement);
16657         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16658         // this copies styles from the containing element into thsi one..
16659         // not sure why we need all of this..
16660         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16661         
16662         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16663         //ss['background-attachment'] = 'fixed'; // w3c
16664         dbody.bgProperties = 'fixed'; // ie
16665         //Roo.DomHelper.applyStyles(dbody, ss);
16666         Roo.EventManager.on(this.doc, {
16667             //'mousedown': this.onEditorEvent,
16668             'mouseup': this.onEditorEvent,
16669             'dblclick': this.onEditorEvent,
16670             'click': this.onEditorEvent,
16671             'keyup': this.onEditorEvent,
16672             buffer:100,
16673             scope: this
16674         });
16675         if(Roo.isGecko){
16676             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16677         }
16678         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16679             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16680         }
16681         this.initialized = true;
16682
16683         this.owner.fireEvent('initialize', this);
16684         this.pushValue();
16685     },
16686
16687     // private
16688     onDestroy : function(){
16689         
16690         
16691         
16692         if(this.rendered){
16693             
16694             //for (var i =0; i < this.toolbars.length;i++) {
16695             //    // fixme - ask toolbars for heights?
16696             //    this.toolbars[i].onDestroy();
16697            // }
16698             
16699             //this.wrap.dom.innerHTML = '';
16700             //this.wrap.remove();
16701         }
16702     },
16703
16704     // private
16705     onFirstFocus : function(){
16706         
16707         this.assignDocWin();
16708         
16709         
16710         this.activated = true;
16711          
16712     
16713         if(Roo.isGecko){ // prevent silly gecko errors
16714             this.win.focus();
16715             var s = this.win.getSelection();
16716             if(!s.focusNode || s.focusNode.nodeType != 3){
16717                 var r = s.getRangeAt(0);
16718                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16719                 r.collapse(true);
16720                 this.deferFocus();
16721             }
16722             try{
16723                 this.execCmd('useCSS', true);
16724                 this.execCmd('styleWithCSS', false);
16725             }catch(e){}
16726         }
16727         this.owner.fireEvent('activate', this);
16728     },
16729
16730     // private
16731     adjustFont: function(btn){
16732         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16733         //if(Roo.isSafari){ // safari
16734         //    adjust *= 2;
16735        // }
16736         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16737         if(Roo.isSafari){ // safari
16738             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16739             v =  (v < 10) ? 10 : v;
16740             v =  (v > 48) ? 48 : v;
16741             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16742             
16743         }
16744         
16745         
16746         v = Math.max(1, v+adjust);
16747         
16748         this.execCmd('FontSize', v  );
16749     },
16750
16751     onEditorEvent : function(e){
16752         this.owner.fireEvent('editorevent', this, e);
16753       //  this.updateToolbar();
16754         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16755     },
16756
16757     insertTag : function(tg)
16758     {
16759         // could be a bit smarter... -> wrap the current selected tRoo..
16760         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16761             
16762             range = this.createRange(this.getSelection());
16763             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16764             wrappingNode.appendChild(range.extractContents());
16765             range.insertNode(wrappingNode);
16766
16767             return;
16768             
16769             
16770             
16771         }
16772         this.execCmd("formatblock",   tg);
16773         
16774     },
16775     
16776     insertText : function(txt)
16777     {
16778         
16779         
16780         var range = this.createRange();
16781         range.deleteContents();
16782                //alert(Sender.getAttribute('label'));
16783                
16784         range.insertNode(this.doc.createTextNode(txt));
16785     } ,
16786     
16787      
16788
16789     /**
16790      * Executes a Midas editor command on the editor document and performs necessary focus and
16791      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16792      * @param {String} cmd The Midas command
16793      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16794      */
16795     relayCmd : function(cmd, value){
16796         this.win.focus();
16797         this.execCmd(cmd, value);
16798         this.owner.fireEvent('editorevent', this);
16799         //this.updateToolbar();
16800         this.owner.deferFocus();
16801     },
16802
16803     /**
16804      * Executes a Midas editor command directly on the editor document.
16805      * For visual commands, you should use {@link #relayCmd} instead.
16806      * <b>This should only be called after the editor is initialized.</b>
16807      * @param {String} cmd The Midas command
16808      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16809      */
16810     execCmd : function(cmd, value){
16811         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16812         this.syncValue();
16813     },
16814  
16815  
16816    
16817     /**
16818      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16819      * to insert tRoo.
16820      * @param {String} text | dom node.. 
16821      */
16822     insertAtCursor : function(text)
16823     {
16824         
16825         
16826         
16827         if(!this.activated){
16828             return;
16829         }
16830         /*
16831         if(Roo.isIE){
16832             this.win.focus();
16833             var r = this.doc.selection.createRange();
16834             if(r){
16835                 r.collapse(true);
16836                 r.pasteHTML(text);
16837                 this.syncValue();
16838                 this.deferFocus();
16839             
16840             }
16841             return;
16842         }
16843         */
16844         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16845             this.win.focus();
16846             
16847             
16848             // from jquery ui (MIT licenced)
16849             var range, node;
16850             var win = this.win;
16851             
16852             if (win.getSelection && win.getSelection().getRangeAt) {
16853                 range = win.getSelection().getRangeAt(0);
16854                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16855                 range.insertNode(node);
16856             } else if (win.document.selection && win.document.selection.createRange) {
16857                 // no firefox support
16858                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16859                 win.document.selection.createRange().pasteHTML(txt);
16860             } else {
16861                 // no firefox support
16862                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16863                 this.execCmd('InsertHTML', txt);
16864             } 
16865             
16866             this.syncValue();
16867             
16868             this.deferFocus();
16869         }
16870     },
16871  // private
16872     mozKeyPress : function(e){
16873         if(e.ctrlKey){
16874             var c = e.getCharCode(), cmd;
16875           
16876             if(c > 0){
16877                 c = String.fromCharCode(c).toLowerCase();
16878                 switch(c){
16879                     case 'b':
16880                         cmd = 'bold';
16881                         break;
16882                     case 'i':
16883                         cmd = 'italic';
16884                         break;
16885                     
16886                     case 'u':
16887                         cmd = 'underline';
16888                         break;
16889                     
16890                     case 'v':
16891                         this.cleanUpPaste.defer(100, this);
16892                         return;
16893                         
16894                 }
16895                 if(cmd){
16896                     this.win.focus();
16897                     this.execCmd(cmd);
16898                     this.deferFocus();
16899                     e.preventDefault();
16900                 }
16901                 
16902             }
16903         }
16904     },
16905
16906     // private
16907     fixKeys : function(){ // load time branching for fastest keydown performance
16908         if(Roo.isIE){
16909             return function(e){
16910                 var k = e.getKey(), r;
16911                 if(k == e.TAB){
16912                     e.stopEvent();
16913                     r = this.doc.selection.createRange();
16914                     if(r){
16915                         r.collapse(true);
16916                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16917                         this.deferFocus();
16918                     }
16919                     return;
16920                 }
16921                 
16922                 if(k == e.ENTER){
16923                     r = this.doc.selection.createRange();
16924                     if(r){
16925                         var target = r.parentElement();
16926                         if(!target || target.tagName.toLowerCase() != 'li'){
16927                             e.stopEvent();
16928                             r.pasteHTML('<br />');
16929                             r.collapse(false);
16930                             r.select();
16931                         }
16932                     }
16933                 }
16934                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16935                     this.cleanUpPaste.defer(100, this);
16936                     return;
16937                 }
16938                 
16939                 
16940             };
16941         }else if(Roo.isOpera){
16942             return function(e){
16943                 var k = e.getKey();
16944                 if(k == e.TAB){
16945                     e.stopEvent();
16946                     this.win.focus();
16947                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16948                     this.deferFocus();
16949                 }
16950                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16951                     this.cleanUpPaste.defer(100, this);
16952                     return;
16953                 }
16954                 
16955             };
16956         }else if(Roo.isSafari){
16957             return function(e){
16958                 var k = e.getKey();
16959                 
16960                 if(k == e.TAB){
16961                     e.stopEvent();
16962                     this.execCmd('InsertText','\t');
16963                     this.deferFocus();
16964                     return;
16965                 }
16966                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16967                     this.cleanUpPaste.defer(100, this);
16968                     return;
16969                 }
16970                 
16971              };
16972         }
16973     }(),
16974     
16975     getAllAncestors: function()
16976     {
16977         var p = this.getSelectedNode();
16978         var a = [];
16979         if (!p) {
16980             a.push(p); // push blank onto stack..
16981             p = this.getParentElement();
16982         }
16983         
16984         
16985         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16986             a.push(p);
16987             p = p.parentNode;
16988         }
16989         a.push(this.doc.body);
16990         return a;
16991     },
16992     lastSel : false,
16993     lastSelNode : false,
16994     
16995     
16996     getSelection : function() 
16997     {
16998         this.assignDocWin();
16999         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17000     },
17001     
17002     getSelectedNode: function() 
17003     {
17004         // this may only work on Gecko!!!
17005         
17006         // should we cache this!!!!
17007         
17008         
17009         
17010          
17011         var range = this.createRange(this.getSelection()).cloneRange();
17012         
17013         if (Roo.isIE) {
17014             var parent = range.parentElement();
17015             while (true) {
17016                 var testRange = range.duplicate();
17017                 testRange.moveToElementText(parent);
17018                 if (testRange.inRange(range)) {
17019                     break;
17020                 }
17021                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17022                     break;
17023                 }
17024                 parent = parent.parentElement;
17025             }
17026             return parent;
17027         }
17028         
17029         // is ancestor a text element.
17030         var ac =  range.commonAncestorContainer;
17031         if (ac.nodeType == 3) {
17032             ac = ac.parentNode;
17033         }
17034         
17035         var ar = ac.childNodes;
17036          
17037         var nodes = [];
17038         var other_nodes = [];
17039         var has_other_nodes = false;
17040         for (var i=0;i<ar.length;i++) {
17041             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17042                 continue;
17043             }
17044             // fullly contained node.
17045             
17046             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17047                 nodes.push(ar[i]);
17048                 continue;
17049             }
17050             
17051             // probably selected..
17052             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17053                 other_nodes.push(ar[i]);
17054                 continue;
17055             }
17056             // outer..
17057             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17058                 continue;
17059             }
17060             
17061             
17062             has_other_nodes = true;
17063         }
17064         if (!nodes.length && other_nodes.length) {
17065             nodes= other_nodes;
17066         }
17067         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17068             return false;
17069         }
17070         
17071         return nodes[0];
17072     },
17073     createRange: function(sel)
17074     {
17075         // this has strange effects when using with 
17076         // top toolbar - not sure if it's a great idea.
17077         //this.editor.contentWindow.focus();
17078         if (typeof sel != "undefined") {
17079             try {
17080                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17081             } catch(e) {
17082                 return this.doc.createRange();
17083             }
17084         } else {
17085             return this.doc.createRange();
17086         }
17087     },
17088     getParentElement: function()
17089     {
17090         
17091         this.assignDocWin();
17092         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17093         
17094         var range = this.createRange(sel);
17095          
17096         try {
17097             var p = range.commonAncestorContainer;
17098             while (p.nodeType == 3) { // text node
17099                 p = p.parentNode;
17100             }
17101             return p;
17102         } catch (e) {
17103             return null;
17104         }
17105     
17106     },
17107     /***
17108      *
17109      * Range intersection.. the hard stuff...
17110      *  '-1' = before
17111      *  '0' = hits..
17112      *  '1' = after.
17113      *         [ -- selected range --- ]
17114      *   [fail]                        [fail]
17115      *
17116      *    basically..
17117      *      if end is before start or  hits it. fail.
17118      *      if start is after end or hits it fail.
17119      *
17120      *   if either hits (but other is outside. - then it's not 
17121      *   
17122      *    
17123      **/
17124     
17125     
17126     // @see http://www.thismuchiknow.co.uk/?p=64.
17127     rangeIntersectsNode : function(range, node)
17128     {
17129         var nodeRange = node.ownerDocument.createRange();
17130         try {
17131             nodeRange.selectNode(node);
17132         } catch (e) {
17133             nodeRange.selectNodeContents(node);
17134         }
17135     
17136         var rangeStartRange = range.cloneRange();
17137         rangeStartRange.collapse(true);
17138     
17139         var rangeEndRange = range.cloneRange();
17140         rangeEndRange.collapse(false);
17141     
17142         var nodeStartRange = nodeRange.cloneRange();
17143         nodeStartRange.collapse(true);
17144     
17145         var nodeEndRange = nodeRange.cloneRange();
17146         nodeEndRange.collapse(false);
17147     
17148         return rangeStartRange.compareBoundaryPoints(
17149                  Range.START_TO_START, nodeEndRange) == -1 &&
17150                rangeEndRange.compareBoundaryPoints(
17151                  Range.START_TO_START, nodeStartRange) == 1;
17152         
17153          
17154     },
17155     rangeCompareNode : function(range, node)
17156     {
17157         var nodeRange = node.ownerDocument.createRange();
17158         try {
17159             nodeRange.selectNode(node);
17160         } catch (e) {
17161             nodeRange.selectNodeContents(node);
17162         }
17163         
17164         
17165         range.collapse(true);
17166     
17167         nodeRange.collapse(true);
17168      
17169         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17170         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17171          
17172         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17173         
17174         var nodeIsBefore   =  ss == 1;
17175         var nodeIsAfter    = ee == -1;
17176         
17177         if (nodeIsBefore && nodeIsAfter)
17178             return 0; // outer
17179         if (!nodeIsBefore && nodeIsAfter)
17180             return 1; //right trailed.
17181         
17182         if (nodeIsBefore && !nodeIsAfter)
17183             return 2;  // left trailed.
17184         // fully contined.
17185         return 3;
17186     },
17187
17188     // private? - in a new class?
17189     cleanUpPaste :  function()
17190     {
17191         // cleans up the whole document..
17192         Roo.log('cleanuppaste');
17193         
17194         this.cleanUpChildren(this.doc.body);
17195         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17196         if (clean != this.doc.body.innerHTML) {
17197             this.doc.body.innerHTML = clean;
17198         }
17199         
17200     },
17201     
17202     cleanWordChars : function(input) {// change the chars to hex code
17203         var he = Roo.HtmlEditorCore;
17204         
17205         var output = input;
17206         Roo.each(he.swapCodes, function(sw) { 
17207             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17208             
17209             output = output.replace(swapper, sw[1]);
17210         });
17211         
17212         return output;
17213     },
17214     
17215     
17216     cleanUpChildren : function (n)
17217     {
17218         if (!n.childNodes.length) {
17219             return;
17220         }
17221         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17222            this.cleanUpChild(n.childNodes[i]);
17223         }
17224     },
17225     
17226     
17227         
17228     
17229     cleanUpChild : function (node)
17230     {
17231         var ed = this;
17232         //console.log(node);
17233         if (node.nodeName == "#text") {
17234             // clean up silly Windows -- stuff?
17235             return; 
17236         }
17237         if (node.nodeName == "#comment") {
17238             node.parentNode.removeChild(node);
17239             // clean up silly Windows -- stuff?
17240             return; 
17241         }
17242         
17243         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17244             // remove node.
17245             node.parentNode.removeChild(node);
17246             return;
17247             
17248         }
17249         
17250         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17251         
17252         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17253         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17254         
17255         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17256         //    remove_keep_children = true;
17257         //}
17258         
17259         if (remove_keep_children) {
17260             this.cleanUpChildren(node);
17261             // inserts everything just before this node...
17262             while (node.childNodes.length) {
17263                 var cn = node.childNodes[0];
17264                 node.removeChild(cn);
17265                 node.parentNode.insertBefore(cn, node);
17266             }
17267             node.parentNode.removeChild(node);
17268             return;
17269         }
17270         
17271         if (!node.attributes || !node.attributes.length) {
17272             this.cleanUpChildren(node);
17273             return;
17274         }
17275         
17276         function cleanAttr(n,v)
17277         {
17278             
17279             if (v.match(/^\./) || v.match(/^\//)) {
17280                 return;
17281             }
17282             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17283                 return;
17284             }
17285             if (v.match(/^#/)) {
17286                 return;
17287             }
17288 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17289             node.removeAttribute(n);
17290             
17291         }
17292         
17293         function cleanStyle(n,v)
17294         {
17295             if (v.match(/expression/)) { //XSS?? should we even bother..
17296                 node.removeAttribute(n);
17297                 return;
17298             }
17299             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17300             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17301             
17302             
17303             var parts = v.split(/;/);
17304             var clean = [];
17305             
17306             Roo.each(parts, function(p) {
17307                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17308                 if (!p.length) {
17309                     return true;
17310                 }
17311                 var l = p.split(':').shift().replace(/\s+/g,'');
17312                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17313                 
17314                 if ( cblack.indexOf(l) > -1) {
17315 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17316                     //node.removeAttribute(n);
17317                     return true;
17318                 }
17319                 //Roo.log()
17320                 // only allow 'c whitelisted system attributes'
17321                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17322 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17323                     //node.removeAttribute(n);
17324                     return true;
17325                 }
17326                 
17327                 
17328                  
17329                 
17330                 clean.push(p);
17331                 return true;
17332             });
17333             if (clean.length) { 
17334                 node.setAttribute(n, clean.join(';'));
17335             } else {
17336                 node.removeAttribute(n);
17337             }
17338             
17339         }
17340         
17341         
17342         for (var i = node.attributes.length-1; i > -1 ; i--) {
17343             var a = node.attributes[i];
17344             //console.log(a);
17345             
17346             if (a.name.toLowerCase().substr(0,2)=='on')  {
17347                 node.removeAttribute(a.name);
17348                 continue;
17349             }
17350             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17351                 node.removeAttribute(a.name);
17352                 continue;
17353             }
17354             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17355                 cleanAttr(a.name,a.value); // fixme..
17356                 continue;
17357             }
17358             if (a.name == 'style') {
17359                 cleanStyle(a.name,a.value);
17360                 continue;
17361             }
17362             /// clean up MS crap..
17363             // tecnically this should be a list of valid class'es..
17364             
17365             
17366             if (a.name == 'class') {
17367                 if (a.value.match(/^Mso/)) {
17368                     node.className = '';
17369                 }
17370                 
17371                 if (a.value.match(/body/)) {
17372                     node.className = '';
17373                 }
17374                 continue;
17375             }
17376             
17377             // style cleanup!?
17378             // class cleanup?
17379             
17380         }
17381         
17382         
17383         this.cleanUpChildren(node);
17384         
17385         
17386     },
17387     /**
17388      * Clean up MS wordisms...
17389      */
17390     cleanWord : function(node)
17391     {
17392         var _t = this;
17393         var cleanWordChildren = function()
17394         {
17395             if (!node.childNodes.length) {
17396                 return;
17397             }
17398             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17399                _t.cleanWord(node.childNodes[i]);
17400             }
17401         }
17402         
17403         
17404         if (!node) {
17405             this.cleanWord(this.doc.body);
17406             return;
17407         }
17408         if (node.nodeName == "#text") {
17409             // clean up silly Windows -- stuff?
17410             return; 
17411         }
17412         if (node.nodeName == "#comment") {
17413             node.parentNode.removeChild(node);
17414             // clean up silly Windows -- stuff?
17415             return; 
17416         }
17417         
17418         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17419             node.parentNode.removeChild(node);
17420             return;
17421         }
17422         
17423         // remove - but keep children..
17424         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17425             while (node.childNodes.length) {
17426                 var cn = node.childNodes[0];
17427                 node.removeChild(cn);
17428                 node.parentNode.insertBefore(cn, node);
17429             }
17430             node.parentNode.removeChild(node);
17431             cleanWordChildren();
17432             return;
17433         }
17434         // clean styles
17435         if (node.className.length) {
17436             
17437             var cn = node.className.split(/\W+/);
17438             var cna = [];
17439             Roo.each(cn, function(cls) {
17440                 if (cls.match(/Mso[a-zA-Z]+/)) {
17441                     return;
17442                 }
17443                 cna.push(cls);
17444             });
17445             node.className = cna.length ? cna.join(' ') : '';
17446             if (!cna.length) {
17447                 node.removeAttribute("class");
17448             }
17449         }
17450         
17451         if (node.hasAttribute("lang")) {
17452             node.removeAttribute("lang");
17453         }
17454         
17455         if (node.hasAttribute("style")) {
17456             
17457             var styles = node.getAttribute("style").split(";");
17458             var nstyle = [];
17459             Roo.each(styles, function(s) {
17460                 if (!s.match(/:/)) {
17461                     return;
17462                 }
17463                 var kv = s.split(":");
17464                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17465                     return;
17466                 }
17467                 // what ever is left... we allow.
17468                 nstyle.push(s);
17469             });
17470             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17471             if (!nstyle.length) {
17472                 node.removeAttribute('style');
17473             }
17474         }
17475         
17476         cleanWordChildren();
17477         
17478         
17479     },
17480     domToHTML : function(currentElement, depth, nopadtext) {
17481         
17482             depth = depth || 0;
17483             nopadtext = nopadtext || false;
17484         
17485             if (!currentElement) {
17486                 return this.domToHTML(this.doc.body);
17487             }
17488             
17489             //Roo.log(currentElement);
17490             var j;
17491             var allText = false;
17492             var nodeName = currentElement.nodeName;
17493             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17494             
17495             if  (nodeName == '#text') {
17496                 return currentElement.nodeValue;
17497             }
17498             
17499             
17500             var ret = '';
17501             if (nodeName != 'BODY') {
17502                  
17503                 var i = 0;
17504                 // Prints the node tagName, such as <A>, <IMG>, etc
17505                 if (tagName) {
17506                     var attr = [];
17507                     for(i = 0; i < currentElement.attributes.length;i++) {
17508                         // quoting?
17509                         var aname = currentElement.attributes.item(i).name;
17510                         if (!currentElement.attributes.item(i).value.length) {
17511                             continue;
17512                         }
17513                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17514                     }
17515                     
17516                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17517                 } 
17518                 else {
17519                     
17520                     // eack
17521                 }
17522             } else {
17523                 tagName = false;
17524             }
17525             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17526                 return ret;
17527             }
17528             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17529                 nopadtext = true;
17530             }
17531             
17532             
17533             // Traverse the tree
17534             i = 0;
17535             var currentElementChild = currentElement.childNodes.item(i);
17536             var allText = true;
17537             var innerHTML  = '';
17538             lastnode = '';
17539             while (currentElementChild) {
17540                 // Formatting code (indent the tree so it looks nice on the screen)
17541                 var nopad = nopadtext;
17542                 if (lastnode == 'SPAN') {
17543                     nopad  = true;
17544                 }
17545                 // text
17546                 if  (currentElementChild.nodeName == '#text') {
17547                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17548                     if (!nopad && toadd.length > 80) {
17549                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17550                     }
17551                     innerHTML  += toadd;
17552                     
17553                     i++;
17554                     currentElementChild = currentElement.childNodes.item(i);
17555                     lastNode = '';
17556                     continue;
17557                 }
17558                 allText = false;
17559                 
17560                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17561                     
17562                 // Recursively traverse the tree structure of the child node
17563                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17564                 lastnode = currentElementChild.nodeName;
17565                 i++;
17566                 currentElementChild=currentElement.childNodes.item(i);
17567             }
17568             
17569             ret += innerHTML;
17570             
17571             if (!allText) {
17572                     // The remaining code is mostly for formatting the tree
17573                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17574             }
17575             
17576             
17577             if (tagName) {
17578                 ret+= "</"+tagName+">";
17579             }
17580             return ret;
17581             
17582         }
17583     
17584     // hide stuff that is not compatible
17585     /**
17586      * @event blur
17587      * @hide
17588      */
17589     /**
17590      * @event change
17591      * @hide
17592      */
17593     /**
17594      * @event focus
17595      * @hide
17596      */
17597     /**
17598      * @event specialkey
17599      * @hide
17600      */
17601     /**
17602      * @cfg {String} fieldClass @hide
17603      */
17604     /**
17605      * @cfg {String} focusClass @hide
17606      */
17607     /**
17608      * @cfg {String} autoCreate @hide
17609      */
17610     /**
17611      * @cfg {String} inputType @hide
17612      */
17613     /**
17614      * @cfg {String} invalidClass @hide
17615      */
17616     /**
17617      * @cfg {String} invalidText @hide
17618      */
17619     /**
17620      * @cfg {String} msgFx @hide
17621      */
17622     /**
17623      * @cfg {String} validateOnBlur @hide
17624      */
17625 });
17626
17627 Roo.HtmlEditorCore.white = [
17628         'area', 'br', 'img', 'input', 'hr', 'wbr',
17629         
17630        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17631        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17632        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17633        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17634        'table',   'ul',         'xmp', 
17635        
17636        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17637       'thead',   'tr', 
17638      
17639       'dir', 'menu', 'ol', 'ul', 'dl',
17640        
17641       'embed',  'object'
17642 ];
17643
17644
17645 Roo.HtmlEditorCore.black = [
17646     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17647         'applet', // 
17648         'base',   'basefont', 'bgsound', 'blink',  'body', 
17649         'frame',  'frameset', 'head',    'html',   'ilayer', 
17650         'iframe', 'layer',  'link',     'meta',    'object',   
17651         'script', 'style' ,'title',  'xml' // clean later..
17652 ];
17653 Roo.HtmlEditorCore.clean = [
17654     'script', 'style', 'title', 'xml'
17655 ];
17656 Roo.HtmlEditorCore.remove = [
17657     'font'
17658 ];
17659 // attributes..
17660
17661 Roo.HtmlEditorCore.ablack = [
17662     'on'
17663 ];
17664     
17665 Roo.HtmlEditorCore.aclean = [ 
17666     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17667 ];
17668
17669 // protocols..
17670 Roo.HtmlEditorCore.pwhite= [
17671         'http',  'https',  'mailto'
17672 ];
17673
17674 // white listed style attributes.
17675 Roo.HtmlEditorCore.cwhite= [
17676       //  'text-align', /// default is to allow most things..
17677       
17678          
17679 //        'font-size'//??
17680 ];
17681
17682 // black listed style attributes.
17683 Roo.HtmlEditorCore.cblack= [
17684       //  'font-size' -- this can be set by the project 
17685 ];
17686
17687
17688 Roo.HtmlEditorCore.swapCodes   =[ 
17689     [    8211, "--" ], 
17690     [    8212, "--" ], 
17691     [    8216,  "'" ],  
17692     [    8217, "'" ],  
17693     [    8220, '"' ],  
17694     [    8221, '"' ],  
17695     [    8226, "*" ],  
17696     [    8230, "..." ]
17697 ]; 
17698
17699     /*
17700  * - LGPL
17701  *
17702  * HtmlEditor
17703  * 
17704  */
17705
17706 /**
17707  * @class Roo.bootstrap.HtmlEditor
17708  * @extends Roo.bootstrap.TextArea
17709  * Bootstrap HtmlEditor class
17710
17711  * @constructor
17712  * Create a new HtmlEditor
17713  * @param {Object} config The config object
17714  */
17715
17716 Roo.bootstrap.HtmlEditor = function(config){
17717     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17718     if (!this.toolbars) {
17719         this.toolbars = [];
17720     }
17721     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17722     this.addEvents({
17723             /**
17724              * @event initialize
17725              * Fires when the editor is fully initialized (including the iframe)
17726              * @param {HtmlEditor} this
17727              */
17728             initialize: true,
17729             /**
17730              * @event activate
17731              * Fires when the editor is first receives the focus. Any insertion must wait
17732              * until after this event.
17733              * @param {HtmlEditor} this
17734              */
17735             activate: true,
17736              /**
17737              * @event beforesync
17738              * Fires before the textarea is updated with content from the editor iframe. Return false
17739              * to cancel the sync.
17740              * @param {HtmlEditor} this
17741              * @param {String} html
17742              */
17743             beforesync: true,
17744              /**
17745              * @event beforepush
17746              * Fires before the iframe editor is updated with content from the textarea. Return false
17747              * to cancel the push.
17748              * @param {HtmlEditor} this
17749              * @param {String} html
17750              */
17751             beforepush: true,
17752              /**
17753              * @event sync
17754              * Fires when the textarea is updated with content from the editor iframe.
17755              * @param {HtmlEditor} this
17756              * @param {String} html
17757              */
17758             sync: true,
17759              /**
17760              * @event push
17761              * Fires when the iframe editor is updated with content from the textarea.
17762              * @param {HtmlEditor} this
17763              * @param {String} html
17764              */
17765             push: true,
17766              /**
17767              * @event editmodechange
17768              * Fires when the editor switches edit modes
17769              * @param {HtmlEditor} this
17770              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17771              */
17772             editmodechange: true,
17773             /**
17774              * @event editorevent
17775              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17776              * @param {HtmlEditor} this
17777              */
17778             editorevent: true,
17779             /**
17780              * @event firstfocus
17781              * Fires when on first focus - needed by toolbars..
17782              * @param {HtmlEditor} this
17783              */
17784             firstfocus: true,
17785             /**
17786              * @event autosave
17787              * Auto save the htmlEditor value as a file into Events
17788              * @param {HtmlEditor} this
17789              */
17790             autosave: true,
17791             /**
17792              * @event savedpreview
17793              * preview the saved version of htmlEditor
17794              * @param {HtmlEditor} this
17795              */
17796             savedpreview: true
17797         });
17798 };
17799
17800
17801 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17802     
17803     
17804       /**
17805      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17806      */
17807     toolbars : false,
17808    
17809      /**
17810      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17811      *                        Roo.resizable.
17812      */
17813     resizable : false,
17814      /**
17815      * @cfg {Number} height (in pixels)
17816      */   
17817     height: 300,
17818    /**
17819      * @cfg {Number} width (in pixels)
17820      */   
17821     width: false,
17822     
17823     /**
17824      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17825      * 
17826      */
17827     stylesheets: false,
17828     
17829     // id of frame..
17830     frameId: false,
17831     
17832     // private properties
17833     validationEvent : false,
17834     deferHeight: true,
17835     initialized : false,
17836     activated : false,
17837     
17838     onFocus : Roo.emptyFn,
17839     iframePad:3,
17840     hideMode:'offsets',
17841     
17842     
17843     tbContainer : false,
17844     
17845     toolbarContainer :function() {
17846         return this.wrap.select('.x-html-editor-tb',true).first();
17847     },
17848
17849     /**
17850      * Protected method that will not generally be called directly. It
17851      * is called when the editor creates its toolbar. Override this method if you need to
17852      * add custom toolbar buttons.
17853      * @param {HtmlEditor} editor
17854      */
17855     createToolbar : function(){
17856         
17857         Roo.log("create toolbars");
17858         
17859         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17860         this.toolbars[0].render(this.toolbarContainer());
17861         
17862         return;
17863         
17864 //        if (!editor.toolbars || !editor.toolbars.length) {
17865 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17866 //        }
17867 //        
17868 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17869 //            editor.toolbars[i] = Roo.factory(
17870 //                    typeof(editor.toolbars[i]) == 'string' ?
17871 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17872 //                Roo.bootstrap.HtmlEditor);
17873 //            editor.toolbars[i].init(editor);
17874 //        }
17875     },
17876
17877      
17878     // private
17879     onRender : function(ct, position)
17880     {
17881        // Roo.log("Call onRender: " + this.xtype);
17882         var _t = this;
17883         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17884       
17885         this.wrap = this.inputEl().wrap({
17886             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17887         });
17888         
17889         this.editorcore.onRender(ct, position);
17890          
17891         if (this.resizable) {
17892             this.resizeEl = new Roo.Resizable(this.wrap, {
17893                 pinned : true,
17894                 wrap: true,
17895                 dynamic : true,
17896                 minHeight : this.height,
17897                 height: this.height,
17898                 handles : this.resizable,
17899                 width: this.width,
17900                 listeners : {
17901                     resize : function(r, w, h) {
17902                         _t.onResize(w,h); // -something
17903                     }
17904                 }
17905             });
17906             
17907         }
17908         this.createToolbar(this);
17909        
17910         
17911         if(!this.width && this.resizable){
17912             this.setSize(this.wrap.getSize());
17913         }
17914         if (this.resizeEl) {
17915             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17916             // should trigger onReize..
17917         }
17918         
17919     },
17920
17921     // private
17922     onResize : function(w, h)
17923     {
17924         Roo.log('resize: ' +w + ',' + h );
17925         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17926         var ew = false;
17927         var eh = false;
17928         
17929         if(this.inputEl() ){
17930             if(typeof w == 'number'){
17931                 var aw = w - this.wrap.getFrameWidth('lr');
17932                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17933                 ew = aw;
17934             }
17935             if(typeof h == 'number'){
17936                  var tbh = -11;  // fixme it needs to tool bar size!
17937                 for (var i =0; i < this.toolbars.length;i++) {
17938                     // fixme - ask toolbars for heights?
17939                     tbh += this.toolbars[i].el.getHeight();
17940                     //if (this.toolbars[i].footer) {
17941                     //    tbh += this.toolbars[i].footer.el.getHeight();
17942                     //}
17943                 }
17944               
17945                 
17946                 
17947                 
17948                 
17949                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17950                 ah -= 5; // knock a few pixes off for look..
17951                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17952                 var eh = ah;
17953             }
17954         }
17955         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17956         this.editorcore.onResize(ew,eh);
17957         
17958     },
17959
17960     /**
17961      * Toggles the editor between standard and source edit mode.
17962      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17963      */
17964     toggleSourceEdit : function(sourceEditMode)
17965     {
17966         this.editorcore.toggleSourceEdit(sourceEditMode);
17967         
17968         if(this.editorcore.sourceEditMode){
17969             Roo.log('editor - showing textarea');
17970             
17971 //            Roo.log('in');
17972 //            Roo.log(this.syncValue());
17973             this.syncValue();
17974             this.inputEl().removeClass(['hide', 'x-hidden']);
17975             this.inputEl().dom.removeAttribute('tabIndex');
17976             this.inputEl().focus();
17977         }else{
17978             Roo.log('editor - hiding textarea');
17979 //            Roo.log('out')
17980 //            Roo.log(this.pushValue()); 
17981             this.pushValue();
17982             
17983             this.inputEl().addClass(['hide', 'x-hidden']);
17984             this.inputEl().dom.setAttribute('tabIndex', -1);
17985             //this.deferFocus();
17986         }
17987          
17988         if(this.resizable){
17989             this.setSize(this.wrap.getSize());
17990         }
17991         
17992         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17993     },
17994  
17995     // private (for BoxComponent)
17996     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17997
17998     // private (for BoxComponent)
17999     getResizeEl : function(){
18000         return this.wrap;
18001     },
18002
18003     // private (for BoxComponent)
18004     getPositionEl : function(){
18005         return this.wrap;
18006     },
18007
18008     // private
18009     initEvents : function(){
18010         this.originalValue = this.getValue();
18011     },
18012
18013 //    /**
18014 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18015 //     * @method
18016 //     */
18017 //    markInvalid : Roo.emptyFn,
18018 //    /**
18019 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18020 //     * @method
18021 //     */
18022 //    clearInvalid : Roo.emptyFn,
18023
18024     setValue : function(v){
18025         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18026         this.editorcore.pushValue();
18027     },
18028
18029      
18030     // private
18031     deferFocus : function(){
18032         this.focus.defer(10, this);
18033     },
18034
18035     // doc'ed in Field
18036     focus : function(){
18037         this.editorcore.focus();
18038         
18039     },
18040       
18041
18042     // private
18043     onDestroy : function(){
18044         
18045         
18046         
18047         if(this.rendered){
18048             
18049             for (var i =0; i < this.toolbars.length;i++) {
18050                 // fixme - ask toolbars for heights?
18051                 this.toolbars[i].onDestroy();
18052             }
18053             
18054             this.wrap.dom.innerHTML = '';
18055             this.wrap.remove();
18056         }
18057     },
18058
18059     // private
18060     onFirstFocus : function(){
18061         //Roo.log("onFirstFocus");
18062         this.editorcore.onFirstFocus();
18063          for (var i =0; i < this.toolbars.length;i++) {
18064             this.toolbars[i].onFirstFocus();
18065         }
18066         
18067     },
18068     
18069     // private
18070     syncValue : function()
18071     {   
18072         this.editorcore.syncValue();
18073     },
18074     
18075     pushValue : function()
18076     {   
18077         this.editorcore.pushValue();
18078     }
18079      
18080     
18081     // hide stuff that is not compatible
18082     /**
18083      * @event blur
18084      * @hide
18085      */
18086     /**
18087      * @event change
18088      * @hide
18089      */
18090     /**
18091      * @event focus
18092      * @hide
18093      */
18094     /**
18095      * @event specialkey
18096      * @hide
18097      */
18098     /**
18099      * @cfg {String} fieldClass @hide
18100      */
18101     /**
18102      * @cfg {String} focusClass @hide
18103      */
18104     /**
18105      * @cfg {String} autoCreate @hide
18106      */
18107     /**
18108      * @cfg {String} inputType @hide
18109      */
18110     /**
18111      * @cfg {String} invalidClass @hide
18112      */
18113     /**
18114      * @cfg {String} invalidText @hide
18115      */
18116     /**
18117      * @cfg {String} msgFx @hide
18118      */
18119     /**
18120      * @cfg {String} validateOnBlur @hide
18121      */
18122 });
18123  
18124     
18125    
18126    
18127    
18128       
18129 Roo.namespace('Roo.bootstrap.htmleditor');
18130 /**
18131  * @class Roo.bootstrap.HtmlEditorToolbar1
18132  * Basic Toolbar
18133  * 
18134  * Usage:
18135  *
18136  new Roo.bootstrap.HtmlEditor({
18137     ....
18138     toolbars : [
18139         new Roo.bootstrap.HtmlEditorToolbar1({
18140             disable : { fonts: 1 , format: 1, ..., ... , ...],
18141             btns : [ .... ]
18142         })
18143     }
18144      
18145  * 
18146  * @cfg {Object} disable List of elements to disable..
18147  * @cfg {Array} btns List of additional buttons.
18148  * 
18149  * 
18150  * NEEDS Extra CSS? 
18151  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18152  */
18153  
18154 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18155 {
18156     
18157     Roo.apply(this, config);
18158     
18159     // default disabled, based on 'good practice'..
18160     this.disable = this.disable || {};
18161     Roo.applyIf(this.disable, {
18162         fontSize : true,
18163         colors : true,
18164         specialElements : true
18165     });
18166     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18167     
18168     this.editor = config.editor;
18169     this.editorcore = config.editor.editorcore;
18170     
18171     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18172     
18173     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18174     // dont call parent... till later.
18175 }
18176 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18177      
18178     bar : true,
18179     
18180     editor : false,
18181     editorcore : false,
18182     
18183     
18184     formats : [
18185         "p" ,  
18186         "h1","h2","h3","h4","h5","h6", 
18187         "pre", "code", 
18188         "abbr", "acronym", "address", "cite", "samp", "var",
18189         'div','span'
18190     ],
18191     
18192     onRender : function(ct, position)
18193     {
18194        // Roo.log("Call onRender: " + this.xtype);
18195         
18196        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18197        Roo.log(this.el);
18198        this.el.dom.style.marginBottom = '0';
18199        var _this = this;
18200        var editorcore = this.editorcore;
18201        var editor= this.editor;
18202        
18203        var children = [];
18204        var btn = function(id,cmd , toggle, handler){
18205        
18206             var  event = toggle ? 'toggle' : 'click';
18207        
18208             var a = {
18209                 size : 'sm',
18210                 xtype: 'Button',
18211                 xns: Roo.bootstrap,
18212                 glyphicon : id,
18213                 cmd : id || cmd,
18214                 enableToggle:toggle !== false,
18215                 //html : 'submit'
18216                 pressed : toggle ? false : null,
18217                 listeners : {}
18218             }
18219             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18220                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18221             }
18222             children.push(a);
18223             return a;
18224        }
18225         
18226         var style = {
18227                 xtype: 'Button',
18228                 size : 'sm',
18229                 xns: Roo.bootstrap,
18230                 glyphicon : 'font',
18231                 //html : 'submit'
18232                 menu : {
18233                     xtype: 'Menu',
18234                     xns: Roo.bootstrap,
18235                     items:  []
18236                 }
18237         };
18238         Roo.each(this.formats, function(f) {
18239             style.menu.items.push({
18240                 xtype :'MenuItem',
18241                 xns: Roo.bootstrap,
18242                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18243                 tagname : f,
18244                 listeners : {
18245                     click : function()
18246                     {
18247                         editorcore.insertTag(this.tagname);
18248                         editor.focus();
18249                     }
18250                 }
18251                 
18252             });
18253         });
18254          children.push(style);   
18255             
18256             
18257         btn('bold',false,true);
18258         btn('italic',false,true);
18259         btn('align-left', 'justifyleft',true);
18260         btn('align-center', 'justifycenter',true);
18261         btn('align-right' , 'justifyright',true);
18262         btn('link', false, false, function(btn) {
18263             //Roo.log("create link?");
18264             var url = prompt(this.createLinkText, this.defaultLinkValue);
18265             if(url && url != 'http:/'+'/'){
18266                 this.editorcore.relayCmd('createlink', url);
18267             }
18268         }),
18269         btn('list','insertunorderedlist',true);
18270         btn('pencil', false,true, function(btn){
18271                 Roo.log(this);
18272                 
18273                 this.toggleSourceEdit(btn.pressed);
18274         });
18275         /*
18276         var cog = {
18277                 xtype: 'Button',
18278                 size : 'sm',
18279                 xns: Roo.bootstrap,
18280                 glyphicon : 'cog',
18281                 //html : 'submit'
18282                 menu : {
18283                     xtype: 'Menu',
18284                     xns: Roo.bootstrap,
18285                     items:  []
18286                 }
18287         };
18288         
18289         cog.menu.items.push({
18290             xtype :'MenuItem',
18291             xns: Roo.bootstrap,
18292             html : Clean styles,
18293             tagname : f,
18294             listeners : {
18295                 click : function()
18296                 {
18297                     editorcore.insertTag(this.tagname);
18298                     editor.focus();
18299                 }
18300             }
18301             
18302         });
18303        */
18304         
18305          
18306        this.xtype = 'NavSimplebar';
18307         
18308         for(var i=0;i< children.length;i++) {
18309             
18310             this.buttons.add(this.addxtypeChild(children[i]));
18311             
18312         }
18313         
18314         editor.on('editorevent', this.updateToolbar, this);
18315     },
18316     onBtnClick : function(id)
18317     {
18318        this.editorcore.relayCmd(id);
18319        this.editorcore.focus();
18320     },
18321     
18322     /**
18323      * Protected method that will not generally be called directly. It triggers
18324      * a toolbar update by reading the markup state of the current selection in the editor.
18325      */
18326     updateToolbar: function(){
18327
18328         if(!this.editorcore.activated){
18329             this.editor.onFirstFocus(); // is this neeed?
18330             return;
18331         }
18332
18333         var btns = this.buttons; 
18334         var doc = this.editorcore.doc;
18335         btns.get('bold').setActive(doc.queryCommandState('bold'));
18336         btns.get('italic').setActive(doc.queryCommandState('italic'));
18337         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18338         
18339         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18340         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18341         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18342         
18343         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18344         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18345          /*
18346         
18347         var ans = this.editorcore.getAllAncestors();
18348         if (this.formatCombo) {
18349             
18350             
18351             var store = this.formatCombo.store;
18352             this.formatCombo.setValue("");
18353             for (var i =0; i < ans.length;i++) {
18354                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18355                     // select it..
18356                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18357                     break;
18358                 }
18359             }
18360         }
18361         
18362         
18363         
18364         // hides menus... - so this cant be on a menu...
18365         Roo.bootstrap.MenuMgr.hideAll();
18366         */
18367         Roo.bootstrap.MenuMgr.hideAll();
18368         //this.editorsyncValue();
18369     },
18370     onFirstFocus: function() {
18371         this.buttons.each(function(item){
18372            item.enable();
18373         });
18374     },
18375     toggleSourceEdit : function(sourceEditMode){
18376         
18377           
18378         if(sourceEditMode){
18379             Roo.log("disabling buttons");
18380            this.buttons.each( function(item){
18381                 if(item.cmd != 'pencil'){
18382                     item.disable();
18383                 }
18384             });
18385           
18386         }else{
18387             Roo.log("enabling buttons");
18388             if(this.editorcore.initialized){
18389                 this.buttons.each( function(item){
18390                     item.enable();
18391                 });
18392             }
18393             
18394         }
18395         Roo.log("calling toggole on editor");
18396         // tell the editor that it's been pressed..
18397         this.editor.toggleSourceEdit(sourceEditMode);
18398        
18399     }
18400 });
18401
18402
18403
18404
18405
18406 /**
18407  * @class Roo.bootstrap.Table.AbstractSelectionModel
18408  * @extends Roo.util.Observable
18409  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18410  * implemented by descendant classes.  This class should not be directly instantiated.
18411  * @constructor
18412  */
18413 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18414     this.locked = false;
18415     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18416 };
18417
18418
18419 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18420     /** @ignore Called by the grid automatically. Do not call directly. */
18421     init : function(grid){
18422         this.grid = grid;
18423         this.initEvents();
18424     },
18425
18426     /**
18427      * Locks the selections.
18428      */
18429     lock : function(){
18430         this.locked = true;
18431     },
18432
18433     /**
18434      * Unlocks the selections.
18435      */
18436     unlock : function(){
18437         this.locked = false;
18438     },
18439
18440     /**
18441      * Returns true if the selections are locked.
18442      * @return {Boolean}
18443      */
18444     isLocked : function(){
18445         return this.locked;
18446     }
18447 });
18448 /**
18449  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18450  * @class Roo.bootstrap.Table.RowSelectionModel
18451  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18452  * It supports multiple selections and keyboard selection/navigation. 
18453  * @constructor
18454  * @param {Object} config
18455  */
18456
18457 Roo.bootstrap.Table.RowSelectionModel = function(config){
18458     Roo.apply(this, config);
18459     this.selections = new Roo.util.MixedCollection(false, function(o){
18460         return o.id;
18461     });
18462
18463     this.last = false;
18464     this.lastActive = false;
18465
18466     this.addEvents({
18467         /**
18468              * @event selectionchange
18469              * Fires when the selection changes
18470              * @param {SelectionModel} this
18471              */
18472             "selectionchange" : true,
18473         /**
18474              * @event afterselectionchange
18475              * Fires after the selection changes (eg. by key press or clicking)
18476              * @param {SelectionModel} this
18477              */
18478             "afterselectionchange" : true,
18479         /**
18480              * @event beforerowselect
18481              * Fires when a row is selected being selected, return false to cancel.
18482              * @param {SelectionModel} this
18483              * @param {Number} rowIndex The selected index
18484              * @param {Boolean} keepExisting False if other selections will be cleared
18485              */
18486             "beforerowselect" : true,
18487         /**
18488              * @event rowselect
18489              * Fires when a row is selected.
18490              * @param {SelectionModel} this
18491              * @param {Number} rowIndex The selected index
18492              * @param {Roo.data.Record} r The record
18493              */
18494             "rowselect" : true,
18495         /**
18496              * @event rowdeselect
18497              * Fires when a row is deselected.
18498              * @param {SelectionModel} this
18499              * @param {Number} rowIndex The selected index
18500              */
18501         "rowdeselect" : true
18502     });
18503     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18504     this.locked = false;
18505 };
18506
18507 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18508     /**
18509      * @cfg {Boolean} singleSelect
18510      * True to allow selection of only one row at a time (defaults to false)
18511      */
18512     singleSelect : false,
18513
18514     // private
18515     initEvents : function(){
18516
18517         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18518             this.grid.on("mousedown", this.handleMouseDown, this);
18519         }else{ // allow click to work like normal
18520             this.grid.on("rowclick", this.handleDragableRowClick, this);
18521         }
18522
18523         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18524             "up" : function(e){
18525                 if(!e.shiftKey){
18526                     this.selectPrevious(e.shiftKey);
18527                 }else if(this.last !== false && this.lastActive !== false){
18528                     var last = this.last;
18529                     this.selectRange(this.last,  this.lastActive-1);
18530                     this.grid.getView().focusRow(this.lastActive);
18531                     if(last !== false){
18532                         this.last = last;
18533                     }
18534                 }else{
18535                     this.selectFirstRow();
18536                 }
18537                 this.fireEvent("afterselectionchange", this);
18538             },
18539             "down" : function(e){
18540                 if(!e.shiftKey){
18541                     this.selectNext(e.shiftKey);
18542                 }else if(this.last !== false && this.lastActive !== false){
18543                     var last = this.last;
18544                     this.selectRange(this.last,  this.lastActive+1);
18545                     this.grid.getView().focusRow(this.lastActive);
18546                     if(last !== false){
18547                         this.last = last;
18548                     }
18549                 }else{
18550                     this.selectFirstRow();
18551                 }
18552                 this.fireEvent("afterselectionchange", this);
18553             },
18554             scope: this
18555         });
18556
18557         var view = this.grid.view;
18558         view.on("refresh", this.onRefresh, this);
18559         view.on("rowupdated", this.onRowUpdated, this);
18560         view.on("rowremoved", this.onRemove, this);
18561     },
18562
18563     // private
18564     onRefresh : function(){
18565         var ds = this.grid.dataSource, i, v = this.grid.view;
18566         var s = this.selections;
18567         s.each(function(r){
18568             if((i = ds.indexOfId(r.id)) != -1){
18569                 v.onRowSelect(i);
18570             }else{
18571                 s.remove(r);
18572             }
18573         });
18574     },
18575
18576     // private
18577     onRemove : function(v, index, r){
18578         this.selections.remove(r);
18579     },
18580
18581     // private
18582     onRowUpdated : function(v, index, r){
18583         if(this.isSelected(r)){
18584             v.onRowSelect(index);
18585         }
18586     },
18587
18588     /**
18589      * Select records.
18590      * @param {Array} records The records to select
18591      * @param {Boolean} keepExisting (optional) True to keep existing selections
18592      */
18593     selectRecords : function(records, keepExisting){
18594         if(!keepExisting){
18595             this.clearSelections();
18596         }
18597         var ds = this.grid.dataSource;
18598         for(var i = 0, len = records.length; i < len; i++){
18599             this.selectRow(ds.indexOf(records[i]), true);
18600         }
18601     },
18602
18603     /**
18604      * Gets the number of selected rows.
18605      * @return {Number}
18606      */
18607     getCount : function(){
18608         return this.selections.length;
18609     },
18610
18611     /**
18612      * Selects the first row in the grid.
18613      */
18614     selectFirstRow : function(){
18615         this.selectRow(0);
18616     },
18617
18618     /**
18619      * Select the last row.
18620      * @param {Boolean} keepExisting (optional) True to keep existing selections
18621      */
18622     selectLastRow : function(keepExisting){
18623         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18624     },
18625
18626     /**
18627      * Selects the row immediately following the last selected row.
18628      * @param {Boolean} keepExisting (optional) True to keep existing selections
18629      */
18630     selectNext : function(keepExisting){
18631         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18632             this.selectRow(this.last+1, keepExisting);
18633             this.grid.getView().focusRow(this.last);
18634         }
18635     },
18636
18637     /**
18638      * Selects the row that precedes the last selected row.
18639      * @param {Boolean} keepExisting (optional) True to keep existing selections
18640      */
18641     selectPrevious : function(keepExisting){
18642         if(this.last){
18643             this.selectRow(this.last-1, keepExisting);
18644             this.grid.getView().focusRow(this.last);
18645         }
18646     },
18647
18648     /**
18649      * Returns the selected records
18650      * @return {Array} Array of selected records
18651      */
18652     getSelections : function(){
18653         return [].concat(this.selections.items);
18654     },
18655
18656     /**
18657      * Returns the first selected record.
18658      * @return {Record}
18659      */
18660     getSelected : function(){
18661         return this.selections.itemAt(0);
18662     },
18663
18664
18665     /**
18666      * Clears all selections.
18667      */
18668     clearSelections : function(fast){
18669         if(this.locked) return;
18670         if(fast !== true){
18671             var ds = this.grid.dataSource;
18672             var s = this.selections;
18673             s.each(function(r){
18674                 this.deselectRow(ds.indexOfId(r.id));
18675             }, this);
18676             s.clear();
18677         }else{
18678             this.selections.clear();
18679         }
18680         this.last = false;
18681     },
18682
18683
18684     /**
18685      * Selects all rows.
18686      */
18687     selectAll : function(){
18688         if(this.locked) return;
18689         this.selections.clear();
18690         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18691             this.selectRow(i, true);
18692         }
18693     },
18694
18695     /**
18696      * Returns True if there is a selection.
18697      * @return {Boolean}
18698      */
18699     hasSelection : function(){
18700         return this.selections.length > 0;
18701     },
18702
18703     /**
18704      * Returns True if the specified row is selected.
18705      * @param {Number/Record} record The record or index of the record to check
18706      * @return {Boolean}
18707      */
18708     isSelected : function(index){
18709         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18710         return (r && this.selections.key(r.id) ? true : false);
18711     },
18712
18713     /**
18714      * Returns True if the specified record id is selected.
18715      * @param {String} id The id of record to check
18716      * @return {Boolean}
18717      */
18718     isIdSelected : function(id){
18719         return (this.selections.key(id) ? true : false);
18720     },
18721
18722     // private
18723     handleMouseDown : function(e, t){
18724         var view = this.grid.getView(), rowIndex;
18725         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18726             return;
18727         };
18728         if(e.shiftKey && this.last !== false){
18729             var last = this.last;
18730             this.selectRange(last, rowIndex, e.ctrlKey);
18731             this.last = last; // reset the last
18732             view.focusRow(rowIndex);
18733         }else{
18734             var isSelected = this.isSelected(rowIndex);
18735             if(e.button !== 0 && isSelected){
18736                 view.focusRow(rowIndex);
18737             }else if(e.ctrlKey && isSelected){
18738                 this.deselectRow(rowIndex);
18739             }else if(!isSelected){
18740                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18741                 view.focusRow(rowIndex);
18742             }
18743         }
18744         this.fireEvent("afterselectionchange", this);
18745     },
18746     // private
18747     handleDragableRowClick :  function(grid, rowIndex, e) 
18748     {
18749         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18750             this.selectRow(rowIndex, false);
18751             grid.view.focusRow(rowIndex);
18752              this.fireEvent("afterselectionchange", this);
18753         }
18754     },
18755     
18756     /**
18757      * Selects multiple rows.
18758      * @param {Array} rows Array of the indexes of the row to select
18759      * @param {Boolean} keepExisting (optional) True to keep existing selections
18760      */
18761     selectRows : function(rows, keepExisting){
18762         if(!keepExisting){
18763             this.clearSelections();
18764         }
18765         for(var i = 0, len = rows.length; i < len; i++){
18766             this.selectRow(rows[i], true);
18767         }
18768     },
18769
18770     /**
18771      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18772      * @param {Number} startRow The index of the first row in the range
18773      * @param {Number} endRow The index of the last row in the range
18774      * @param {Boolean} keepExisting (optional) True to retain existing selections
18775      */
18776     selectRange : function(startRow, endRow, keepExisting){
18777         if(this.locked) return;
18778         if(!keepExisting){
18779             this.clearSelections();
18780         }
18781         if(startRow <= endRow){
18782             for(var i = startRow; i <= endRow; i++){
18783                 this.selectRow(i, true);
18784             }
18785         }else{
18786             for(var i = startRow; i >= endRow; i--){
18787                 this.selectRow(i, true);
18788             }
18789         }
18790     },
18791
18792     /**
18793      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18794      * @param {Number} startRow The index of the first row in the range
18795      * @param {Number} endRow The index of the last row in the range
18796      */
18797     deselectRange : function(startRow, endRow, preventViewNotify){
18798         if(this.locked) return;
18799         for(var i = startRow; i <= endRow; i++){
18800             this.deselectRow(i, preventViewNotify);
18801         }
18802     },
18803
18804     /**
18805      * Selects a row.
18806      * @param {Number} row The index of the row to select
18807      * @param {Boolean} keepExisting (optional) True to keep existing selections
18808      */
18809     selectRow : function(index, keepExisting, preventViewNotify){
18810         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18811         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18812             if(!keepExisting || this.singleSelect){
18813                 this.clearSelections();
18814             }
18815             var r = this.grid.dataSource.getAt(index);
18816             this.selections.add(r);
18817             this.last = this.lastActive = index;
18818             if(!preventViewNotify){
18819                 this.grid.getView().onRowSelect(index);
18820             }
18821             this.fireEvent("rowselect", this, index, r);
18822             this.fireEvent("selectionchange", this);
18823         }
18824     },
18825
18826     /**
18827      * Deselects a row.
18828      * @param {Number} row The index of the row to deselect
18829      */
18830     deselectRow : function(index, preventViewNotify){
18831         if(this.locked) return;
18832         if(this.last == index){
18833             this.last = false;
18834         }
18835         if(this.lastActive == index){
18836             this.lastActive = false;
18837         }
18838         var r = this.grid.dataSource.getAt(index);
18839         this.selections.remove(r);
18840         if(!preventViewNotify){
18841             this.grid.getView().onRowDeselect(index);
18842         }
18843         this.fireEvent("rowdeselect", this, index);
18844         this.fireEvent("selectionchange", this);
18845     },
18846
18847     // private
18848     restoreLast : function(){
18849         if(this._last){
18850             this.last = this._last;
18851         }
18852     },
18853
18854     // private
18855     acceptsNav : function(row, col, cm){
18856         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18857     },
18858
18859     // private
18860     onEditorKey : function(field, e){
18861         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18862         if(k == e.TAB){
18863             e.stopEvent();
18864             ed.completeEdit();
18865             if(e.shiftKey){
18866                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18867             }else{
18868                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18869             }
18870         }else if(k == e.ENTER && !e.ctrlKey){
18871             e.stopEvent();
18872             ed.completeEdit();
18873             if(e.shiftKey){
18874                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18875             }else{
18876                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18877             }
18878         }else if(k == e.ESC){
18879             ed.cancelEdit();
18880         }
18881         if(newCell){
18882             g.startEditing(newCell[0], newCell[1]);
18883         }
18884     }
18885 });/*
18886  * Based on:
18887  * Ext JS Library 1.1.1
18888  * Copyright(c) 2006-2007, Ext JS, LLC.
18889  *
18890  * Originally Released Under LGPL - original licence link has changed is not relivant.
18891  *
18892  * Fork - LGPL
18893  * <script type="text/javascript">
18894  */
18895  
18896 /**
18897  * @class Roo.bootstrap.PagingToolbar
18898  * @extends Roo.Row
18899  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18900  * @constructor
18901  * Create a new PagingToolbar
18902  * @param {Object} config The config object
18903  */
18904 Roo.bootstrap.PagingToolbar = function(config)
18905 {
18906     // old args format still supported... - xtype is prefered..
18907         // created from xtype...
18908     var ds = config.dataSource;
18909     this.toolbarItems = [];
18910     if (config.items) {
18911         this.toolbarItems = config.items;
18912 //        config.items = [];
18913     }
18914     
18915     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18916     this.ds = ds;
18917     this.cursor = 0;
18918     if (ds) { 
18919         this.bind(ds);
18920     }
18921     
18922     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18923     
18924 };
18925
18926 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18927     /**
18928      * @cfg {Roo.data.Store} dataSource
18929      * The underlying data store providing the paged data
18930      */
18931     /**
18932      * @cfg {String/HTMLElement/Element} container
18933      * container The id or element that will contain the toolbar
18934      */
18935     /**
18936      * @cfg {Boolean} displayInfo
18937      * True to display the displayMsg (defaults to false)
18938      */
18939     /**
18940      * @cfg {Number} pageSize
18941      * The number of records to display per page (defaults to 20)
18942      */
18943     pageSize: 20,
18944     /**
18945      * @cfg {String} displayMsg
18946      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18947      */
18948     displayMsg : 'Displaying {0} - {1} of {2}',
18949     /**
18950      * @cfg {String} emptyMsg
18951      * The message to display when no records are found (defaults to "No data to display")
18952      */
18953     emptyMsg : 'No data to display',
18954     /**
18955      * Customizable piece of the default paging text (defaults to "Page")
18956      * @type String
18957      */
18958     beforePageText : "Page",
18959     /**
18960      * Customizable piece of the default paging text (defaults to "of %0")
18961      * @type String
18962      */
18963     afterPageText : "of {0}",
18964     /**
18965      * Customizable piece of the default paging text (defaults to "First Page")
18966      * @type String
18967      */
18968     firstText : "First Page",
18969     /**
18970      * Customizable piece of the default paging text (defaults to "Previous Page")
18971      * @type String
18972      */
18973     prevText : "Previous Page",
18974     /**
18975      * Customizable piece of the default paging text (defaults to "Next Page")
18976      * @type String
18977      */
18978     nextText : "Next Page",
18979     /**
18980      * Customizable piece of the default paging text (defaults to "Last Page")
18981      * @type String
18982      */
18983     lastText : "Last Page",
18984     /**
18985      * Customizable piece of the default paging text (defaults to "Refresh")
18986      * @type String
18987      */
18988     refreshText : "Refresh",
18989
18990     buttons : false,
18991     // private
18992     onRender : function(ct, position) 
18993     {
18994         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18995         this.navgroup.parentId = this.id;
18996         this.navgroup.onRender(this.el, null);
18997         // add the buttons to the navgroup
18998         
18999         if(this.displayInfo){
19000             Roo.log(this.el.select('ul.navbar-nav',true).first());
19001             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19002             this.displayEl = this.el.select('.x-paging-info', true).first();
19003 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19004 //            this.displayEl = navel.el.select('span',true).first();
19005         }
19006         
19007         var _this = this;
19008         
19009         if(this.buttons){
19010             Roo.each(_this.buttons, function(e){
19011                Roo.factory(e).onRender(_this.el, null);
19012             });
19013         }
19014             
19015         Roo.each(_this.toolbarItems, function(e) {
19016             _this.navgroup.addItem(e);
19017         });
19018         
19019         this.first = this.navgroup.addItem({
19020             tooltip: this.firstText,
19021             cls: "prev",
19022             icon : 'fa fa-backward',
19023             disabled: true,
19024             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19025         });
19026         
19027         this.prev =  this.navgroup.addItem({
19028             tooltip: this.prevText,
19029             cls: "prev",
19030             icon : 'fa fa-step-backward',
19031             disabled: true,
19032             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19033         });
19034     //this.addSeparator();
19035         
19036         
19037         var field = this.navgroup.addItem( {
19038             tagtype : 'span',
19039             cls : 'x-paging-position',
19040             
19041             html : this.beforePageText  +
19042                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19043                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19044          } ); //?? escaped?
19045         
19046         this.field = field.el.select('input', true).first();
19047         this.field.on("keydown", this.onPagingKeydown, this);
19048         this.field.on("focus", function(){this.dom.select();});
19049     
19050     
19051         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19052         //this.field.setHeight(18);
19053         //this.addSeparator();
19054         this.next = this.navgroup.addItem({
19055             tooltip: this.nextText,
19056             cls: "next",
19057             html : ' <i class="fa fa-step-forward">',
19058             disabled: true,
19059             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19060         });
19061         this.last = this.navgroup.addItem({
19062             tooltip: this.lastText,
19063             icon : 'fa fa-forward',
19064             cls: "next",
19065             disabled: true,
19066             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19067         });
19068     //this.addSeparator();
19069         this.loading = this.navgroup.addItem({
19070             tooltip: this.refreshText,
19071             icon: 'fa fa-refresh',
19072             
19073             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19074         });
19075
19076     },
19077
19078     // private
19079     updateInfo : function(){
19080         if(this.displayEl){
19081             var count = this.ds.getCount();
19082             var msg = count == 0 ?
19083                 this.emptyMsg :
19084                 String.format(
19085                     this.displayMsg,
19086                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19087                 );
19088             this.displayEl.update(msg);
19089         }
19090     },
19091
19092     // private
19093     onLoad : function(ds, r, o){
19094        this.cursor = o.params ? o.params.start : 0;
19095        var d = this.getPageData(),
19096             ap = d.activePage,
19097             ps = d.pages;
19098         
19099        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19100        this.field.dom.value = ap;
19101        this.first.setDisabled(ap == 1);
19102        this.prev.setDisabled(ap == 1);
19103        this.next.setDisabled(ap == ps);
19104        this.last.setDisabled(ap == ps);
19105        this.loading.enable();
19106        this.updateInfo();
19107     },
19108
19109     // private
19110     getPageData : function(){
19111         var total = this.ds.getTotalCount();
19112         return {
19113             total : total,
19114             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19115             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19116         };
19117     },
19118
19119     // private
19120     onLoadError : function(){
19121         this.loading.enable();
19122     },
19123
19124     // private
19125     onPagingKeydown : function(e){
19126         var k = e.getKey();
19127         var d = this.getPageData();
19128         if(k == e.RETURN){
19129             var v = this.field.dom.value, pageNum;
19130             if(!v || isNaN(pageNum = parseInt(v, 10))){
19131                 this.field.dom.value = d.activePage;
19132                 return;
19133             }
19134             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19135             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19136             e.stopEvent();
19137         }
19138         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))
19139         {
19140           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19141           this.field.dom.value = pageNum;
19142           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19143           e.stopEvent();
19144         }
19145         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19146         {
19147           var v = this.field.dom.value, pageNum; 
19148           var increment = (e.shiftKey) ? 10 : 1;
19149           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19150             increment *= -1;
19151           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19152             this.field.dom.value = d.activePage;
19153             return;
19154           }
19155           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19156           {
19157             this.field.dom.value = parseInt(v, 10) + increment;
19158             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19159             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19160           }
19161           e.stopEvent();
19162         }
19163     },
19164
19165     // private
19166     beforeLoad : function(){
19167         if(this.loading){
19168             this.loading.disable();
19169         }
19170     },
19171
19172     // private
19173     onClick : function(which){
19174         var ds = this.ds;
19175         if (!ds) {
19176             return;
19177         }
19178         switch(which){
19179             case "first":
19180                 ds.load({params:{start: 0, limit: this.pageSize}});
19181             break;
19182             case "prev":
19183                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19184             break;
19185             case "next":
19186                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19187             break;
19188             case "last":
19189                 var total = ds.getTotalCount();
19190                 var extra = total % this.pageSize;
19191                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19192                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19193             break;
19194             case "refresh":
19195                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19196             break;
19197         }
19198     },
19199
19200     /**
19201      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19202      * @param {Roo.data.Store} store The data store to unbind
19203      */
19204     unbind : function(ds){
19205         ds.un("beforeload", this.beforeLoad, this);
19206         ds.un("load", this.onLoad, this);
19207         ds.un("loadexception", this.onLoadError, this);
19208         ds.un("remove", this.updateInfo, this);
19209         ds.un("add", this.updateInfo, this);
19210         this.ds = undefined;
19211     },
19212
19213     /**
19214      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19215      * @param {Roo.data.Store} store The data store to bind
19216      */
19217     bind : function(ds){
19218         ds.on("beforeload", this.beforeLoad, this);
19219         ds.on("load", this.onLoad, this);
19220         ds.on("loadexception", this.onLoadError, this);
19221         ds.on("remove", this.updateInfo, this);
19222         ds.on("add", this.updateInfo, this);
19223         this.ds = ds;
19224     }
19225 });/*
19226  * - LGPL
19227  *
19228  * element
19229  * 
19230  */
19231
19232 /**
19233  * @class Roo.bootstrap.MessageBar
19234  * @extends Roo.bootstrap.Component
19235  * Bootstrap MessageBar class
19236  * @cfg {String} html contents of the MessageBar
19237  * @cfg {String} weight (info | success | warning | danger) default info
19238  * @cfg {String} beforeClass insert the bar before the given class
19239  * @cfg {Boolean} closable (true | false) default false
19240  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19241  * 
19242  * @constructor
19243  * Create a new Element
19244  * @param {Object} config The config object
19245  */
19246
19247 Roo.bootstrap.MessageBar = function(config){
19248     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19249 };
19250
19251 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19252     
19253     html: '',
19254     weight: 'info',
19255     closable: false,
19256     fixed: false,
19257     beforeClass: 'bootstrap-sticky-wrap',
19258     
19259     getAutoCreate : function(){
19260         
19261         var cfg = {
19262             tag: 'div',
19263             cls: 'alert alert-dismissable alert-' + this.weight,
19264             cn: [
19265                 {
19266                     tag: 'span',
19267                     cls: 'message',
19268                     html: this.html || ''
19269                 }
19270             ]
19271         }
19272         
19273         if(this.fixed){
19274             cfg.cls += ' alert-messages-fixed';
19275         }
19276         
19277         if(this.closable){
19278             cfg.cn.push({
19279                 tag: 'button',
19280                 cls: 'close',
19281                 html: 'x'
19282             });
19283         }
19284         
19285         return cfg;
19286     },
19287     
19288     onRender : function(ct, position)
19289     {
19290         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19291         
19292         if(!this.el){
19293             var cfg = Roo.apply({},  this.getAutoCreate());
19294             cfg.id = Roo.id();
19295             
19296             if (this.cls) {
19297                 cfg.cls += ' ' + this.cls;
19298             }
19299             if (this.style) {
19300                 cfg.style = this.style;
19301             }
19302             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19303             
19304             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19305         }
19306         
19307         this.el.select('>button.close').on('click', this.hide, this);
19308         
19309     },
19310     
19311     show : function()
19312     {
19313         if (!this.rendered) {
19314             this.render();
19315         }
19316         
19317         this.el.show();
19318         
19319         this.fireEvent('show', this);
19320         
19321     },
19322     
19323     hide : function()
19324     {
19325         if (!this.rendered) {
19326             this.render();
19327         }
19328         
19329         this.el.hide();
19330         
19331         this.fireEvent('hide', this);
19332     },
19333     
19334     update : function()
19335     {
19336 //        var e = this.el.dom.firstChild;
19337 //        
19338 //        if(this.closable){
19339 //            e = e.nextSibling;
19340 //        }
19341 //        
19342 //        e.data = this.html || '';
19343
19344         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19345     }
19346    
19347 });
19348
19349  
19350
19351      /*
19352  * - LGPL
19353  *
19354  * Graph
19355  * 
19356  */
19357
19358
19359 /**
19360  * @class Roo.bootstrap.Graph
19361  * @extends Roo.bootstrap.Component
19362  * Bootstrap Graph class
19363 > Prameters
19364  -sm {number} sm 4
19365  -md {number} md 5
19366  @cfg {String} graphtype  bar | vbar | pie
19367  @cfg {number} g_x coodinator | centre x (pie)
19368  @cfg {number} g_y coodinator | centre y (pie)
19369  @cfg {number} g_r radius (pie)
19370  @cfg {number} g_height height of the chart (respected by all elements in the set)
19371  @cfg {number} g_width width of the chart (respected by all elements in the set)
19372  @cfg {Object} title The title of the chart
19373     
19374  -{Array}  values
19375  -opts (object) options for the chart 
19376      o {
19377      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19378      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19379      o vgutter (number)
19380      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.
19381      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19382      o to
19383      o stretch (boolean)
19384      o }
19385  -opts (object) options for the pie
19386      o{
19387      o cut
19388      o startAngle (number)
19389      o endAngle (number)
19390      } 
19391  *
19392  * @constructor
19393  * Create a new Input
19394  * @param {Object} config The config object
19395  */
19396
19397 Roo.bootstrap.Graph = function(config){
19398     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19399     
19400     this.addEvents({
19401         // img events
19402         /**
19403          * @event click
19404          * The img click event for the img.
19405          * @param {Roo.EventObject} e
19406          */
19407         "click" : true
19408     });
19409 };
19410
19411 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19412     
19413     sm: 4,
19414     md: 5,
19415     graphtype: 'bar',
19416     g_height: 250,
19417     g_width: 400,
19418     g_x: 50,
19419     g_y: 50,
19420     g_r: 30,
19421     opts:{
19422         //g_colors: this.colors,
19423         g_type: 'soft',
19424         g_gutter: '20%'
19425
19426     },
19427     title : false,
19428
19429     getAutoCreate : function(){
19430         
19431         var cfg = {
19432             tag: 'div',
19433             html : null
19434         }
19435         
19436         
19437         return  cfg;
19438     },
19439
19440     onRender : function(ct,position){
19441         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19442         this.raphael = Raphael(this.el.dom);
19443         
19444                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19445                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19446                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19447                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19448                 /*
19449                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19450                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19451                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19452                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19453                 
19454                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19455                 r.barchart(330, 10, 300, 220, data1);
19456                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19457                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19458                 */
19459                 
19460                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19461                 // r.barchart(30, 30, 560, 250,  xdata, {
19462                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19463                 //     axis : "0 0 1 1",
19464                 //     axisxlabels :  xdata
19465                 //     //yvalues : cols,
19466                    
19467                 // });
19468 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19469 //        
19470 //        this.load(null,xdata,{
19471 //                axis : "0 0 1 1",
19472 //                axisxlabels :  xdata
19473 //                });
19474
19475     },
19476
19477     load : function(graphtype,xdata,opts){
19478         this.raphael.clear();
19479         if(!graphtype) {
19480             graphtype = this.graphtype;
19481         }
19482         if(!opts){
19483             opts = this.opts;
19484         }
19485         var r = this.raphael,
19486             fin = function () {
19487                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19488             },
19489             fout = function () {
19490                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19491             },
19492             pfin = function() {
19493                 this.sector.stop();
19494                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19495
19496                 if (this.label) {
19497                     this.label[0].stop();
19498                     this.label[0].attr({ r: 7.5 });
19499                     this.label[1].attr({ "font-weight": 800 });
19500                 }
19501             },
19502             pfout = function() {
19503                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19504
19505                 if (this.label) {
19506                     this.label[0].animate({ r: 5 }, 500, "bounce");
19507                     this.label[1].attr({ "font-weight": 400 });
19508                 }
19509             };
19510
19511         switch(graphtype){
19512             case 'bar':
19513                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19514                 break;
19515             case 'hbar':
19516                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19517                 break;
19518             case 'pie':
19519 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19520 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19521 //            
19522                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19523                 
19524                 break;
19525
19526         }
19527         
19528         if(this.title){
19529             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19530         }
19531         
19532     },
19533     
19534     setTitle: function(o)
19535     {
19536         this.title = o;
19537     },
19538     
19539     initEvents: function() {
19540         
19541         if(!this.href){
19542             this.el.on('click', this.onClick, this);
19543         }
19544     },
19545     
19546     onClick : function(e)
19547     {
19548         Roo.log('img onclick');
19549         this.fireEvent('click', this, e);
19550     }
19551    
19552 });
19553
19554  
19555 /*
19556  * - LGPL
19557  *
19558  * numberBox
19559  * 
19560  */
19561 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19562
19563 /**
19564  * @class Roo.bootstrap.dash.NumberBox
19565  * @extends Roo.bootstrap.Component
19566  * Bootstrap NumberBox class
19567  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19568  * @cfg {String} headline Box headline
19569  * @cfg {String} content Box content
19570  * @cfg {String} icon Box icon
19571  * @cfg {String} footer Footer text
19572  * @cfg {String} fhref Footer href
19573  * 
19574  * @constructor
19575  * Create a new NumberBox
19576  * @param {Object} config The config object
19577  */
19578
19579
19580 Roo.bootstrap.dash.NumberBox = function(config){
19581     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19582     
19583 };
19584
19585 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19586     
19587     bgcolor : 'aqua',
19588     headline : '',
19589     content : '',
19590     icon : '',
19591     footer : '',
19592     fhref : '',
19593     ficon : '',
19594     
19595     getAutoCreate : function(){
19596         
19597         var cfg = {
19598             tag : 'div',
19599             cls : 'small-box bg-' + this.bgcolor,
19600             cn : [
19601                 {
19602                     tag : 'div',
19603                     cls : 'inner',
19604                     cn :[
19605                         {
19606                             tag : 'h3',
19607                             cls : 'roo-headline',
19608                             html : this.headline
19609                         },
19610                         {
19611                             tag : 'p',
19612                             cls : 'roo-content',
19613                             html : this.content
19614                         }
19615                     ]
19616                 }
19617             ]
19618         }
19619         
19620         if(this.icon){
19621             cfg.cn.push({
19622                 tag : 'div',
19623                 cls : 'icon',
19624                 cn :[
19625                     {
19626                         tag : 'i',
19627                         cls : 'ion ' + this.icon
19628                     }
19629                 ]
19630             });
19631         }
19632         
19633         if(this.footer){
19634             var footer = {
19635                 tag : 'a',
19636                 cls : 'small-box-footer',
19637                 href : this.fhref || '#',
19638                 html : this.footer
19639             };
19640             
19641             cfg.cn.push(footer);
19642             
19643         }
19644         
19645         return  cfg;
19646     },
19647
19648     onRender : function(ct,position){
19649         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19650
19651
19652        
19653                 
19654     },
19655
19656     setHeadline: function (value)
19657     {
19658         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19659     },
19660     
19661     setFooter: function (value, href)
19662     {
19663         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19664         
19665         if(href){
19666             this.el.select('a.small-box-footer',true).first().attr('href', href);
19667         }
19668         
19669     },
19670
19671     setContent: function (value)
19672     {
19673         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19674     },
19675
19676     initEvents: function() 
19677     {   
19678         
19679     }
19680     
19681 });
19682
19683  
19684 /*
19685  * - LGPL
19686  *
19687  * TabBox
19688  * 
19689  */
19690 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19691
19692 /**
19693  * @class Roo.bootstrap.dash.TabBox
19694  * @extends Roo.bootstrap.Component
19695  * Bootstrap TabBox class
19696  * @cfg {String} title Title of the TabBox
19697  * @cfg {String} icon Icon of the TabBox
19698  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19699  * 
19700  * @constructor
19701  * Create a new TabBox
19702  * @param {Object} config The config object
19703  */
19704
19705
19706 Roo.bootstrap.dash.TabBox = function(config){
19707     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19708     this.addEvents({
19709         // raw events
19710         /**
19711          * @event addpane
19712          * When a pane is added
19713          * @param {Roo.bootstrap.dash.TabPane} pane
19714          */
19715         "addpane" : true
19716          
19717     });
19718 };
19719
19720 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19721
19722     title : '',
19723     icon : false,
19724     showtabs : true,
19725     
19726     getChildContainer : function()
19727     {
19728         return this.el.select('.tab-content', true).first();
19729     },
19730     
19731     getAutoCreate : function(){
19732         
19733         var header = {
19734             tag: 'li',
19735             cls: 'pull-left header',
19736             html: this.title,
19737             cn : []
19738         };
19739         
19740         if(this.icon){
19741             header.cn.push({
19742                 tag: 'i',
19743                 cls: 'fa ' + this.icon
19744             });
19745         }
19746         
19747         
19748         var cfg = {
19749             tag: 'div',
19750             cls: 'nav-tabs-custom',
19751             cn: [
19752                 {
19753                     tag: 'ul',
19754                     cls: 'nav nav-tabs pull-right',
19755                     cn: [
19756                         header
19757                     ]
19758                 },
19759                 {
19760                     tag: 'div',
19761                     cls: 'tab-content no-padding',
19762                     cn: []
19763                 }
19764             ]
19765         }
19766
19767         return  cfg;
19768     },
19769     initEvents : function()
19770     {
19771         //Roo.log('add add pane handler');
19772         this.on('addpane', this.onAddPane, this);
19773     },
19774      /**
19775      * Updates the box title
19776      * @param {String} html to set the title to.
19777      */
19778     setTitle : function(value)
19779     {
19780         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19781     },
19782     onAddPane : function(pane)
19783     {
19784         //Roo.log('addpane');
19785         //Roo.log(pane);
19786         // tabs are rendere left to right..
19787         if(!this.showtabs){
19788             return;
19789         }
19790         
19791         var ctr = this.el.select('.nav-tabs', true).first();
19792          
19793          
19794         var existing = ctr.select('.nav-tab',true);
19795         var qty = existing.getCount();;
19796         
19797         
19798         var tab = ctr.createChild({
19799             tag : 'li',
19800             cls : 'nav-tab' + (qty ? '' : ' active'),
19801             cn : [
19802                 {
19803                     tag : 'a',
19804                     href:'#',
19805                     html : pane.title
19806                 }
19807             ]
19808         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19809         pane.tab = tab;
19810         
19811         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19812         if (!qty) {
19813             pane.el.addClass('active');
19814         }
19815         
19816                 
19817     },
19818     onTabClick : function(ev,un,ob,pane)
19819     {
19820         //Roo.log('tab - prev default');
19821         ev.preventDefault();
19822         
19823         
19824         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19825         pane.tab.addClass('active');
19826         //Roo.log(pane.title);
19827         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19828         // technically we should have a deactivate event.. but maybe add later.
19829         // and it should not de-activate the selected tab...
19830         
19831         pane.el.addClass('active');
19832         pane.fireEvent('activate');
19833         
19834         
19835     }
19836     
19837     
19838 });
19839
19840  
19841 /*
19842  * - LGPL
19843  *
19844  * Tab pane
19845  * 
19846  */
19847 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19848 /**
19849  * @class Roo.bootstrap.TabPane
19850  * @extends Roo.bootstrap.Component
19851  * Bootstrap TabPane class
19852  * @cfg {Boolean} active (false | true) Default false
19853  * @cfg {String} title title of panel
19854
19855  * 
19856  * @constructor
19857  * Create a new TabPane
19858  * @param {Object} config The config object
19859  */
19860
19861 Roo.bootstrap.dash.TabPane = function(config){
19862     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19863     
19864     this.addEvents({
19865         // raw events
19866         /**
19867          * @event activate
19868          * When a pane is activated
19869          * @param {Roo.bootstrap.dash.TabPane} pane
19870          */
19871         "activate" : true
19872          
19873     });
19874 };
19875
19876 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19877     
19878     active : false,
19879     title : '',
19880     
19881     // the tabBox that this is attached to.
19882     tab : false,
19883      
19884     getAutoCreate : function() 
19885     {
19886         var cfg = {
19887             tag: 'div',
19888             cls: 'tab-pane'
19889         }
19890         
19891         if(this.active){
19892             cfg.cls += ' active';
19893         }
19894         
19895         return cfg;
19896     },
19897     initEvents  : function()
19898     {
19899         //Roo.log('trigger add pane handler');
19900         this.parent().fireEvent('addpane', this)
19901     },
19902     
19903      /**
19904      * Updates the tab title 
19905      * @param {String} html to set the title to.
19906      */
19907     setTitle: function(str)
19908     {
19909         if (!this.tab) {
19910             return;
19911         }
19912         this.title = str;
19913         this.tab.select('a', true).first().dom.innerHTML = str;
19914         
19915     }
19916     
19917     
19918     
19919 });
19920
19921  
19922
19923
19924  /*
19925  * - LGPL
19926  *
19927  * menu
19928  * 
19929  */
19930 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19931
19932 /**
19933  * @class Roo.bootstrap.menu.Menu
19934  * @extends Roo.bootstrap.Component
19935  * Bootstrap Menu class - container for Menu
19936  * @cfg {String} html Text of the menu
19937  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19938  * @cfg {String} icon Font awesome icon
19939  * @cfg {String} pos Menu align to (top | bottom) default bottom
19940  * 
19941  * 
19942  * @constructor
19943  * Create a new Menu
19944  * @param {Object} config The config object
19945  */
19946
19947
19948 Roo.bootstrap.menu.Menu = function(config){
19949     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19950     
19951     this.addEvents({
19952         /**
19953          * @event beforeshow
19954          * Fires before this menu is displayed
19955          * @param {Roo.bootstrap.menu.Menu} this
19956          */
19957         beforeshow : true,
19958         /**
19959          * @event beforehide
19960          * Fires before this menu is hidden
19961          * @param {Roo.bootstrap.menu.Menu} this
19962          */
19963         beforehide : true,
19964         /**
19965          * @event show
19966          * Fires after this menu is displayed
19967          * @param {Roo.bootstrap.menu.Menu} this
19968          */
19969         show : true,
19970         /**
19971          * @event hide
19972          * Fires after this menu is hidden
19973          * @param {Roo.bootstrap.menu.Menu} this
19974          */
19975         hide : true,
19976         /**
19977          * @event click
19978          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19979          * @param {Roo.bootstrap.menu.Menu} this
19980          * @param {Roo.EventObject} e
19981          */
19982         click : true
19983     });
19984     
19985 };
19986
19987 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19988     
19989     submenu : false,
19990     html : '',
19991     weight : 'default',
19992     icon : false,
19993     pos : 'bottom',
19994     
19995     
19996     getChildContainer : function() {
19997         if(this.isSubMenu){
19998             return this.el;
19999         }
20000         
20001         return this.el.select('ul.dropdown-menu', true).first();  
20002     },
20003     
20004     getAutoCreate : function()
20005     {
20006         var text = [
20007             {
20008                 tag : 'span',
20009                 cls : 'roo-menu-text',
20010                 html : this.html
20011             }
20012         ];
20013         
20014         if(this.icon){
20015             text.unshift({
20016                 tag : 'i',
20017                 cls : 'fa ' + this.icon
20018             })
20019         }
20020         
20021         
20022         var cfg = {
20023             tag : 'div',
20024             cls : 'btn-group',
20025             cn : [
20026                 {
20027                     tag : 'button',
20028                     cls : 'dropdown-button btn btn-' + this.weight,
20029                     cn : text
20030                 },
20031                 {
20032                     tag : 'button',
20033                     cls : 'dropdown-toggle btn btn-' + this.weight,
20034                     cn : [
20035                         {
20036                             tag : 'span',
20037                             cls : 'caret'
20038                         }
20039                     ]
20040                 },
20041                 {
20042                     tag : 'ul',
20043                     cls : 'dropdown-menu'
20044                 }
20045             ]
20046             
20047         };
20048         
20049         if(this.pos == 'top'){
20050             cfg.cls += ' dropup';
20051         }
20052         
20053         if(this.isSubMenu){
20054             cfg = {
20055                 tag : 'ul',
20056                 cls : 'dropdown-menu'
20057             }
20058         }
20059         
20060         return cfg;
20061     },
20062     
20063     onRender : function(ct, position)
20064     {
20065         this.isSubMenu = ct.hasClass('dropdown-submenu');
20066         
20067         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20068     },
20069     
20070     initEvents : function() 
20071     {
20072         if(this.isSubMenu){
20073             return;
20074         }
20075         
20076         this.hidden = true;
20077         
20078         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20079         this.triggerEl.on('click', this.onTriggerPress, this);
20080         
20081         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20082         this.buttonEl.on('click', this.onClick, this);
20083         
20084     },
20085     
20086     list : function()
20087     {
20088         if(this.isSubMenu){
20089             return this.el;
20090         }
20091         
20092         return this.el.select('ul.dropdown-menu', true).first();
20093     },
20094     
20095     onClick : function(e)
20096     {
20097         this.fireEvent("click", this, e);
20098     },
20099     
20100     onTriggerPress  : function(e)
20101     {   
20102         if (this.isVisible()) {
20103             this.hide();
20104         } else {
20105             this.show();
20106         }
20107     },
20108     
20109     isVisible : function(){
20110         return !this.hidden;
20111     },
20112     
20113     show : function()
20114     {
20115         this.fireEvent("beforeshow", this);
20116         
20117         this.hidden = false;
20118         this.el.addClass('open');
20119         
20120         Roo.get(document).on("mouseup", this.onMouseUp, this);
20121         
20122         this.fireEvent("show", this);
20123         
20124         
20125     },
20126     
20127     hide : function()
20128     {
20129         this.fireEvent("beforehide", this);
20130         
20131         this.hidden = true;
20132         this.el.removeClass('open');
20133         
20134         Roo.get(document).un("mouseup", this.onMouseUp);
20135         
20136         this.fireEvent("hide", this);
20137     },
20138     
20139     onMouseUp : function()
20140     {
20141         this.hide();
20142     }
20143     
20144 });
20145
20146  
20147  /*
20148  * - LGPL
20149  *
20150  * menu item
20151  * 
20152  */
20153 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20154
20155 /**
20156  * @class Roo.bootstrap.menu.Item
20157  * @extends Roo.bootstrap.Component
20158  * Bootstrap MenuItem class
20159  * @cfg {Boolean} submenu (true | false) default false
20160  * @cfg {String} html text of the item
20161  * @cfg {String} href the link
20162  * @cfg {Boolean} disable (true | false) default false
20163  * @cfg {Boolean} preventDefault (true | false) default true
20164  * @cfg {String} icon Font awesome icon
20165  * @cfg {String} pos Submenu align to (left | right) default right 
20166  * 
20167  * 
20168  * @constructor
20169  * Create a new Item
20170  * @param {Object} config The config object
20171  */
20172
20173
20174 Roo.bootstrap.menu.Item = function(config){
20175     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20176     this.addEvents({
20177         /**
20178          * @event mouseover
20179          * Fires when the mouse is hovering over this menu
20180          * @param {Roo.bootstrap.menu.Item} this
20181          * @param {Roo.EventObject} e
20182          */
20183         mouseover : true,
20184         /**
20185          * @event mouseout
20186          * Fires when the mouse exits this menu
20187          * @param {Roo.bootstrap.menu.Item} this
20188          * @param {Roo.EventObject} e
20189          */
20190         mouseout : true,
20191         // raw events
20192         /**
20193          * @event click
20194          * The raw click event for the entire grid.
20195          * @param {Roo.EventObject} e
20196          */
20197         click : true
20198     });
20199 };
20200
20201 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20202     
20203     submenu : false,
20204     href : '',
20205     html : '',
20206     preventDefault: true,
20207     disable : false,
20208     icon : false,
20209     pos : 'right',
20210     
20211     getAutoCreate : function()
20212     {
20213         var text = [
20214             {
20215                 tag : 'span',
20216                 cls : 'roo-menu-item-text',
20217                 html : this.html
20218             }
20219         ];
20220         
20221         if(this.icon){
20222             text.unshift({
20223                 tag : 'i',
20224                 cls : 'fa ' + this.icon
20225             })
20226         }
20227         
20228         var cfg = {
20229             tag : 'li',
20230             cn : [
20231                 {
20232                     tag : 'a',
20233                     href : this.href || '#',
20234                     cn : text
20235                 }
20236             ]
20237         };
20238         
20239         if(this.disable){
20240             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20241         }
20242         
20243         if(this.submenu){
20244             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20245             
20246             if(this.pos == 'left'){
20247                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20248             }
20249         }
20250         
20251         return cfg;
20252     },
20253     
20254     initEvents : function() 
20255     {
20256         this.el.on('mouseover', this.onMouseOver, this);
20257         this.el.on('mouseout', this.onMouseOut, this);
20258         
20259         this.el.select('a', true).first().on('click', this.onClick, this);
20260         
20261     },
20262     
20263     onClick : function(e)
20264     {
20265         if(this.preventDefault){
20266             e.preventDefault();
20267         }
20268         
20269         this.fireEvent("click", this, e);
20270     },
20271     
20272     onMouseOver : function(e)
20273     {
20274         if(this.submenu && this.pos == 'left'){
20275             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20276         }
20277         
20278         this.fireEvent("mouseover", this, e);
20279     },
20280     
20281     onMouseOut : function(e)
20282     {
20283         this.fireEvent("mouseout", this, e);
20284     }
20285 });
20286
20287  
20288
20289  /*
20290  * - LGPL
20291  *
20292  * menu separator
20293  * 
20294  */
20295 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20296
20297 /**
20298  * @class Roo.bootstrap.menu.Separator
20299  * @extends Roo.bootstrap.Component
20300  * Bootstrap Separator class
20301  * 
20302  * @constructor
20303  * Create a new Separator
20304  * @param {Object} config The config object
20305  */
20306
20307
20308 Roo.bootstrap.menu.Separator = function(config){
20309     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20310 };
20311
20312 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20313     
20314     getAutoCreate : function(){
20315         var cfg = {
20316             tag : 'li',
20317             cls: 'divider'
20318         };
20319         
20320         return cfg;
20321     }
20322    
20323 });
20324
20325  
20326
20327