Roo/bootstrap/menu/Menu.js
[web.Reddit] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr](true));
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         /*
249         if (typeof (tree.menu) != 'undefined') {
250             tree.menu.parentType = cn.xtype;
251             tree.menu.triggerEl = cn.el;
252             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253             
254         }
255         */
256         if (!tree.items || !tree.items.length) {
257             cn.items = nitems;
258             return cn;
259         }
260         var items = tree.items;
261         delete tree.items;
262         
263         //Roo.log(items.length);
264             // add the items..
265         for(var i =0;i < items.length;i++) {
266             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
267         }
268         
269         cn.items = nitems;
270         
271         return cn;
272     }
273     
274     
275     
276     
277 });
278
279  /*
280  * - LGPL
281  *
282  * Body
283  * 
284  */
285
286 /**
287  * @class Roo.bootstrap.Body
288  * @extends Roo.bootstrap.Component
289  * Bootstrap Body class
290  * 
291  * @constructor
292  * Create a new body
293  * @param {Object} config The config object
294  */
295
296 Roo.bootstrap.Body = function(config){
297     Roo.bootstrap.Body.superclass.constructor.call(this, config);
298     this.el = Roo.get(document.body);
299     if (this.cls && this.cls.length) {
300         Roo.get(document.body).addClass(this.cls);
301     }
302 };
303
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
305       
306         autoCreate : {
307         cls: 'container'
308     },
309     onRender : function(ct, position)
310     {
311        /* Roo.log("Roo.bootstrap.Body - onRender");
312         if (this.cls && this.cls.length) {
313             Roo.get(document.body).addClass(this.cls);
314         }
315         // style??? xttr???
316         */
317     }
318     
319     
320  
321    
322 });
323
324  /*
325  * - LGPL
326  *
327  * button group
328  * 
329  */
330
331
332 /**
333  * @class Roo.bootstrap.ButtonGroup
334  * @extends Roo.bootstrap.Component
335  * Bootstrap ButtonGroup class
336  * @cfg {String} size lg | sm | xs (default empty normal)
337  * @cfg {String} align vertical | justified  (default none)
338  * @cfg {String} direction up | down (default down)
339  * @cfg {Boolean} toolbar false | true
340  * @cfg {Boolean} btn true | false
341  * 
342  * 
343  * @constructor
344  * Create a new Input
345  * @param {Object} config The config object
346  */
347
348 Roo.bootstrap.ButtonGroup = function(config){
349     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
350 };
351
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
353     
354     size: '',
355     align: '',
356     direction: '',
357     toolbar: false,
358     btn: true,
359
360     getAutoCreate : function(){
361         var cfg = {
362             cls: 'btn-group',
363             html : null
364         }
365         
366         cfg.html = this.html || cfg.html;
367         
368         if (this.toolbar) {
369             cfg = {
370                 cls: 'btn-toolbar',
371                 html: null
372             }
373             
374             return cfg;
375         }
376         
377         if (['vertical','justified'].indexOf(this.align)!==-1) {
378             cfg.cls = 'btn-group-' + this.align;
379             
380             if (this.align == 'justified') {
381                 console.log(this.items);
382             }
383         }
384         
385         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386             cfg.cls += ' btn-group-' + this.size;
387         }
388         
389         if (this.direction == 'up') {
390             cfg.cls += ' dropup' ;
391         }
392         
393         return cfg;
394     }
395    
396 });
397
398  /*
399  * - LGPL
400  *
401  * button
402  * 
403  */
404
405 /**
406  * @class Roo.bootstrap.Button
407  * @extends Roo.bootstrap.Component
408  * Bootstrap Button class
409  * @cfg {String} html The button content
410  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411  * @cfg {String} size empty | lg | sm | xs
412  * @cfg {String} tag empty | a | input | submit
413  * @cfg {String} href empty or href
414  * @cfg {Boolean} disabled false | true
415  * @cfg {Boolean} isClose false | true
416  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
417  * @cfg {String} badge text for badge
418  * @cfg {String} theme default (or empty) | glow
419  * @cfg {Boolean} inverse false | true
420  * @cfg {Boolean} toggle false | true
421  * @cfg {String} ontext text for on toggle state
422  * @cfg {String} offtext text for off toggle state
423  * @cfg {Boolean} defaulton true | false
424  * @cfg {Boolean} preventDefault (true | false) default true
425  * @cfg {Boolean} removeClass true | false remove the standard class..
426  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
427  * 
428  * @constructor
429  * Create a new button
430  * @param {Object} config The config object
431  */
432
433
434 Roo.bootstrap.Button = function(config){
435     Roo.bootstrap.Button.superclass.constructor.call(this, config);
436     this.addEvents({
437         // raw events
438         /**
439          * @event click
440          * When a butotn is pressed
441          * @param {Roo.EventObject} e
442          */
443         "click" : true,
444          /**
445          * @event toggle
446          * After the button has been toggles
447          * @param {Roo.EventObject} e
448          * @param {boolean} pressed (also available as button.pressed)
449          */
450         "toggle" : true
451     });
452 };
453
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
455     html: false,
456     active: false,
457     weight: '',
458     size: '',
459     tag: 'button',
460     href: '',
461     disabled: false,
462     isClose: false,
463     glyphicon: '',
464     badge: '',
465     theme: 'default',
466     inverse: false,
467     
468     toggle: false,
469     ontext: 'ON',
470     offtext: 'OFF',
471     defaulton: true,
472     preventDefault: true,
473     removeClass: false,
474     name: false,
475     target: false,
476     
477     
478     pressed : null,
479      
480     
481     getAutoCreate : function(){
482         
483         var cfg = {
484             tag : 'button',
485             cls : 'roo-button',
486             html: ''
487         };
488         
489         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
491             this.tag = 'button';
492         } else {
493             cfg.tag = this.tag;
494         }
495         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496         
497         if (this.toggle == true) {
498             cfg={
499                 tag: 'div',
500                 cls: 'slider-frame roo-button',
501                 cn: [
502                     {
503                         tag: 'span',
504                         'data-on-text':'ON',
505                         'data-off-text':'OFF',
506                         cls: 'slider-button',
507                         html: this.offtext
508                     }
509                 ]
510             };
511             
512             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513                 cfg.cls += ' '+this.weight;
514             }
515             
516             return cfg;
517         }
518         
519         if (this.isClose) {
520             cfg.cls += ' close';
521             
522             cfg["aria-hidden"] = true;
523             
524             cfg.html = "&times;";
525             
526             return cfg;
527         }
528         
529          
530         if (this.theme==='default') {
531             cfg.cls = 'btn roo-button';
532             
533             //if (this.parentType != 'Navbar') {
534             this.weight = this.weight.length ?  this.weight : 'default';
535             //}
536             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537                 
538                 cfg.cls += ' btn-' + this.weight;
539             }
540         } else if (this.theme==='glow') {
541             
542             cfg.tag = 'a';
543             cfg.cls = 'btn-glow roo-button';
544             
545             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546                 
547                 cfg.cls += ' ' + this.weight;
548             }
549         }
550    
551         
552         if (this.inverse) {
553             this.cls += ' inverse';
554         }
555         
556         
557         if (this.active) {
558             cfg.cls += ' active';
559         }
560         
561         if (this.disabled) {
562             cfg.disabled = 'disabled';
563         }
564         
565         if (this.items) {
566             Roo.log('changing to ul' );
567             cfg.tag = 'ul';
568             this.glyphicon = 'caret';
569         }
570         
571         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572          
573         //gsRoo.log(this.parentType);
574         if (this.parentType === 'Navbar' && !this.parent().bar) {
575             Roo.log('changing to li?');
576             
577             cfg.tag = 'li';
578             
579             cfg.cls = '';
580             cfg.cn =  [{
581                 tag : 'a',
582                 cls : 'roo-button',
583                 html : this.html,
584                 href : this.href || '#'
585             }];
586             if (this.menu) {
587                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
588                 cfg.cls += ' dropdown';
589             }   
590             
591             delete cfg.html;
592             
593         }
594         
595        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
596         
597         if (this.glyphicon) {
598             cfg.html = ' ' + cfg.html;
599             
600             cfg.cn = [
601                 {
602                     tag: 'span',
603                     cls: 'glyphicon glyphicon-' + this.glyphicon
604                 }
605             ];
606         }
607         
608         if (this.badge) {
609             cfg.html += ' ';
610             
611             cfg.tag = 'a';
612             
613 //            cfg.cls='btn roo-button';
614             
615             cfg.href=this.href;
616             
617             var value = cfg.html;
618             
619             if(this.glyphicon){
620                 value = {
621                             tag: 'span',
622                             cls: 'glyphicon glyphicon-' + this.glyphicon,
623                             html: this.html
624                         };
625                 
626             }
627             
628             cfg.cn = [
629                 value,
630                 {
631                     tag: 'span',
632                     cls: 'badge',
633                     html: this.badge
634                 }
635             ];
636             
637             cfg.html='';
638         }
639         
640         if (this.menu) {
641             cfg.cls += ' dropdown';
642             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
643         }
644         
645         if (cfg.tag !== 'a' && this.href !== '') {
646             throw "Tag must be a to set href.";
647         } else if (this.href.length > 0) {
648             cfg.href = this.href;
649         }
650         
651         if(this.removeClass){
652             cfg.cls = '';
653         }
654         
655         if(this.target){
656             cfg.target = this.target;
657         }
658         
659         return cfg;
660     },
661     initEvents: function() {
662        // Roo.log('init events?');
663 //        Roo.log(this.el.dom);
664         // add the menu...
665         
666         if (typeof (this.menu) != 'undefined') {
667             this.menu.parentType = this.xtype;
668             this.menu.triggerEl = this.el;
669             this.addxtype(Roo.apply({}, this.menu));
670         }
671
672
673        if (this.el.hasClass('roo-button')) {
674             this.el.on('click', this.onClick, this);
675        } else {
676             this.el.select('.roo-button').on('click', this.onClick, this);
677        }
678        
679        if(this.removeClass){
680            this.el.on('click', this.onClick, this);
681        }
682        
683        this.el.enableDisplayMode();
684         
685     },
686     onClick : function(e)
687     {
688         if (this.disabled) {
689             return;
690         }
691         
692         Roo.log('button on click ');
693         if(this.preventDefault){
694             e.preventDefault();
695         }
696         if (this.pressed === true || this.pressed === false) {
697             this.pressed = !this.pressed;
698             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699             this.fireEvent('toggle', this, e, this.pressed);
700         }
701         
702         
703         this.fireEvent('click', this, e);
704     },
705     
706     /**
707      * Enables this button
708      */
709     enable : function()
710     {
711         this.disabled = false;
712         this.el.removeClass('disabled');
713     },
714     
715     /**
716      * Disable this button
717      */
718     disable : function()
719     {
720         this.disabled = true;
721         this.el.addClass('disabled');
722     },
723      /**
724      * sets the active state on/off, 
725      * @param {Boolean} state (optional) Force a particular state
726      */
727     setActive : function(v) {
728         
729         this.el[v ? 'addClass' : 'removeClass']('active');
730     },
731      /**
732      * toggles the current active state 
733      */
734     toggleActive : function()
735     {
736        var active = this.el.hasClass('active');
737        this.setActive(!active);
738        
739         
740     },
741     setText : function(str)
742     {
743         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
744     },
745     getText : function()
746     {
747         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
748     },
749     hide: function() {
750        
751      
752         this.el.hide();   
753     },
754     show: function() {
755        
756         this.el.show();   
757     }
758     
759     
760 });
761
762  /*
763  * - LGPL
764  *
765  * column
766  * 
767  */
768
769 /**
770  * @class Roo.bootstrap.Column
771  * @extends Roo.bootstrap.Component
772  * Bootstrap Column class
773  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
775  * @cfg {Number} md colspan out of 12 for computer-sized screens
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
777  * @cfg {String} html content of column.
778  * 
779  * @constructor
780  * Create a new Column
781  * @param {Object} config The config object
782  */
783
784 Roo.bootstrap.Column = function(config){
785     Roo.bootstrap.Column.superclass.constructor.call(this, config);
786 };
787
788 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
789     
790     xs: null,
791     sm: null,
792     md: null,
793     lg: null,
794     html: '',
795     offset: 0,
796     
797     getAutoCreate : function(){
798         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
799         
800         cfg = {
801             tag: 'div',
802             cls: 'column'
803         };
804         
805         var settings=this;
806         ['xs','sm','md','lg'].map(function(size){
807             if (settings[size]) {
808                 cfg.cls += ' col-' + size + '-' + settings[size];
809             }
810         });
811         if (this.html.length) {
812             cfg.html = this.html;
813         }
814         
815         return cfg;
816     }
817    
818 });
819
820  
821
822  /*
823  * - LGPL
824  *
825  * page container.
826  * 
827  */
828
829
830 /**
831  * @class Roo.bootstrap.Container
832  * @extends Roo.bootstrap.Component
833  * Bootstrap Container class
834  * @cfg {Boolean} jumbotron is it a jumbotron element
835  * @cfg {String} html content of element
836  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
837  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
838  * @cfg {String} header content of header (for panel)
839  * @cfg {String} footer content of footer (for panel)
840  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
841  * @cfg {String} tag (header|aside|section) type of HTML tag.
842
843  *     
844  * @constructor
845  * Create a new Container
846  * @param {Object} config The config object
847  */
848
849 Roo.bootstrap.Container = function(config){
850     Roo.bootstrap.Container.superclass.constructor.call(this, config);
851 };
852
853 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
854     
855     jumbotron : false,
856     well: '',
857     panel : '',
858     header: '',
859     footer : '',
860     sticky: '',
861     tag : false,
862   
863      
864     getChildContainer : function() {
865         
866         if(!this.el){
867             return false;
868         }
869         
870         if (this.panel.length) {
871             return this.el.select('.panel-body',true).first();
872         }
873         
874         return this.el;
875     },
876     
877     
878     getAutoCreate : function(){
879         
880         var cfg = {
881             tag : this.tag || 'div',
882             html : '',
883             cls : ''
884         };
885         if (this.jumbotron) {
886             cfg.cls = 'jumbotron';
887         }
888         // - this is applied by the parent..
889         //if (this.cls) {
890         //    cfg.cls = this.cls + '';
891         //}
892         
893         if (this.sticky.length) {
894             
895             var bd = Roo.get(document.body);
896             if (!bd.hasClass('bootstrap-sticky')) {
897                 bd.addClass('bootstrap-sticky');
898                 Roo.select('html',true).setStyle('height', '100%');
899             }
900              
901             cfg.cls += 'bootstrap-sticky-' + this.sticky;
902         }
903         
904         
905         if (this.well.length) {
906             switch (this.well) {
907                 case 'lg':
908                 case 'sm':
909                     cfg.cls +=' well well-' +this.well;
910                     break;
911                 default:
912                     cfg.cls +=' well';
913                     break;
914             }
915         }
916         
917         var body = cfg;
918         
919         if (this.panel.length) {
920             cfg.cls += ' panel panel-' + this.panel;
921             cfg.cn = [];
922             if (this.header.length) {
923                 cfg.cn.push({
924                     
925                     cls : 'panel-heading',
926                     cn : [{
927                         tag: 'h3',
928                         cls : 'panel-title',
929                         html : this.header
930                     }]
931                     
932                 });
933             }
934             body = false;
935             cfg.cn.push({
936                 cls : 'panel-body',
937                 html : this.html
938             });
939             
940             
941             if (this.footer.length) {
942                 cfg.cn.push({
943                     cls : 'panel-footer',
944                     html : this.footer
945                     
946                 });
947             }
948             
949         }
950         
951         if (body) {
952             body.html = this.html || cfg.html;
953         }
954         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
955             cfg.cls =  'container';
956         }
957         
958         return cfg;
959     },
960     
961     titleEl : function()
962     {
963         if(!this.el || !this.panel.length || !this.header.length){
964             return;
965         }
966         
967         return this.el.select('.panel-title',true).first();
968     },
969     
970     setTitle : function(v)
971     {
972         var titleEl = this.titleEl();
973         
974         if(!titleEl){
975             return;
976         }
977         
978         titleEl.dom.innerHTML = v;
979     },
980     
981     getTitle : function()
982     {
983         
984         var titleEl = this.titleEl();
985         
986         if(!titleEl){
987             return '';
988         }
989         
990         return titleEl.dom.innerHTML;
991     }
992    
993 });
994
995  /*
996  * - LGPL
997  *
998  * image
999  * 
1000  */
1001
1002
1003 /**
1004  * @class Roo.bootstrap.Img
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Img class
1007  * @cfg {Boolean} imgResponsive false | true
1008  * @cfg {String} border rounded | circle | thumbnail
1009  * @cfg {String} src image source
1010  * @cfg {String} alt image alternative text
1011  * @cfg {String} href a tag href
1012  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1013  * 
1014  * @constructor
1015  * Create a new Input
1016  * @param {Object} config The config object
1017  */
1018
1019 Roo.bootstrap.Img = function(config){
1020     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1021     
1022     this.addEvents({
1023         // img events
1024         /**
1025          * @event click
1026          * The img click event for the img.
1027          * @param {Roo.EventObject} e
1028          */
1029         "click" : true
1030     });
1031 };
1032
1033 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1034     
1035     imgResponsive: true,
1036     border: '',
1037     src: '',
1038     href: false,
1039     target: false,
1040
1041     getAutoCreate : function(){
1042         
1043         var cfg = {
1044             tag: 'img',
1045             cls: (this.imgResponsive) ? 'img-responsive' : '',
1046             html : null
1047         }
1048         
1049         cfg.html = this.html || cfg.html;
1050         
1051         cfg.src = this.src || cfg.src;
1052         
1053         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1054             cfg.cls += ' img-' + this.border;
1055         }
1056         
1057         if(this.alt){
1058             cfg.alt = this.alt;
1059         }
1060         
1061         if(this.href){
1062             var a = {
1063                 tag: 'a',
1064                 href: this.href,
1065                 cn: [
1066                     cfg
1067                 ]
1068             }
1069             
1070             if(this.target){
1071                 a.target = this.target;
1072             }
1073             
1074         }
1075         
1076         
1077         return (this.href) ? a : cfg;
1078     },
1079     
1080     initEvents: function() {
1081         
1082         if(!this.href){
1083             this.el.on('click', this.onClick, this);
1084         }
1085     },
1086     
1087     onClick : function(e)
1088     {
1089         Roo.log('img onclick');
1090         this.fireEvent('click', this, e);
1091     }
1092    
1093 });
1094
1095  /*
1096  * - LGPL
1097  *
1098  * image
1099  * 
1100  */
1101
1102
1103 /**
1104  * @class Roo.bootstrap.Link
1105  * @extends Roo.bootstrap.Component
1106  * Bootstrap Link Class
1107  * @cfg {String} alt image alternative text
1108  * @cfg {String} href a tag href
1109  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1110  * @cfg {String} html the content of the link.
1111  * @cfg {Boolean} preventDefault (true | false) default false
1112
1113  * 
1114  * @constructor
1115  * Create a new Input
1116  * @param {Object} config The config object
1117  */
1118
1119 Roo.bootstrap.Link = function(config){
1120     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1121     
1122     this.addEvents({
1123         // img events
1124         /**
1125          * @event click
1126          * The img click event for the img.
1127          * @param {Roo.EventObject} e
1128          */
1129         "click" : true
1130     });
1131 };
1132
1133 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1134     
1135     href: false,
1136     target: false,
1137     preventDefault: false,
1138
1139     getAutoCreate : function(){
1140         
1141         var cfg = {
1142             tag: 'a',
1143             html : this.html || 'html-missing'
1144         }
1145         
1146         
1147         if(this.alt){
1148             cfg.alt = this.alt;
1149         }
1150         cfg.href = this.href || '#';
1151         if(this.target){
1152             cfg.target = this.target;
1153         }
1154         
1155         return cfg;
1156     },
1157     
1158     initEvents: function() {
1159         
1160         if(!this.href){
1161             this.el.on('click', this.onClick, this);
1162         }
1163     },
1164     
1165     onClick : function(e)
1166     {
1167         if(this.preventDefault){
1168             e.preventDefault();
1169         }
1170         //Roo.log('img onclick');
1171         this.fireEvent('click', this, e);
1172     }
1173    
1174 });
1175
1176  /*
1177  * - LGPL
1178  *
1179  * header
1180  * 
1181  */
1182
1183 /**
1184  * @class Roo.bootstrap.Header
1185  * @extends Roo.bootstrap.Component
1186  * Bootstrap Header class
1187  * @cfg {String} html content of header
1188  * @cfg {Number} level (1|2|3|4|5|6) default 1
1189  * 
1190  * @constructor
1191  * Create a new Header
1192  * @param {Object} config The config object
1193  */
1194
1195
1196 Roo.bootstrap.Header  = function(config){
1197     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1198 };
1199
1200 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1201     
1202     //href : false,
1203     html : false,
1204     level : 1,
1205     
1206     
1207     
1208     getAutoCreate : function(){
1209         
1210         var cfg = {
1211             tag: 'h' + (1 *this.level),
1212             html: this.html || 'fill in html'
1213         } ;
1214         
1215         return cfg;
1216     }
1217    
1218 });
1219
1220  
1221
1222  /*
1223  * Based on:
1224  * Ext JS Library 1.1.1
1225  * Copyright(c) 2006-2007, Ext JS, LLC.
1226  *
1227  * Originally Released Under LGPL - original licence link has changed is not relivant.
1228  *
1229  * Fork - LGPL
1230  * <script type="text/javascript">
1231  */
1232  
1233 /**
1234  * @class Roo.bootstrap.MenuMgr
1235  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1236  * @singleton
1237  */
1238 Roo.bootstrap.MenuMgr = function(){
1239    var menus, active, groups = {}, attached = false, lastShow = new Date();
1240
1241    // private - called when first menu is created
1242    function init(){
1243        menus = {};
1244        active = new Roo.util.MixedCollection();
1245        Roo.get(document).addKeyListener(27, function(){
1246            if(active.length > 0){
1247                hideAll();
1248            }
1249        });
1250    }
1251
1252    // private
1253    function hideAll(){
1254        if(active && active.length > 0){
1255            var c = active.clone();
1256            c.each(function(m){
1257                m.hide();
1258            });
1259        }
1260    }
1261
1262    // private
1263    function onHide(m){
1264        active.remove(m);
1265        if(active.length < 1){
1266            Roo.get(document).un("mouseup", onMouseDown);
1267             
1268            attached = false;
1269        }
1270    }
1271
1272    // private
1273    function onShow(m){
1274        var last = active.last();
1275        lastShow = new Date();
1276        active.add(m);
1277        if(!attached){
1278           Roo.get(document).on("mouseup", onMouseDown);
1279            
1280            attached = true;
1281        }
1282        if(m.parentMenu){
1283           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1284           m.parentMenu.activeChild = m;
1285        }else if(last && last.isVisible()){
1286           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1287        }
1288    }
1289
1290    // private
1291    function onBeforeHide(m){
1292        if(m.activeChild){
1293            m.activeChild.hide();
1294        }
1295        if(m.autoHideTimer){
1296            clearTimeout(m.autoHideTimer);
1297            delete m.autoHideTimer;
1298        }
1299    }
1300
1301    // private
1302    function onBeforeShow(m){
1303        var pm = m.parentMenu;
1304        if(!pm && !m.allowOtherMenus){
1305            hideAll();
1306        }else if(pm && pm.activeChild && active != m){
1307            pm.activeChild.hide();
1308        }
1309    }
1310
1311    // private
1312    function onMouseDown(e){
1313         Roo.log("on MouseDown");
1314         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1315            hideAll();
1316         }
1317         
1318         
1319    }
1320
1321    // private
1322    function onBeforeCheck(mi, state){
1323        if(state){
1324            var g = groups[mi.group];
1325            for(var i = 0, l = g.length; i < l; i++){
1326                if(g[i] != mi){
1327                    g[i].setChecked(false);
1328                }
1329            }
1330        }
1331    }
1332
1333    return {
1334
1335        /**
1336         * Hides all menus that are currently visible
1337         */
1338        hideAll : function(){
1339             hideAll();  
1340        },
1341
1342        // private
1343        register : function(menu){
1344            if(!menus){
1345                init();
1346            }
1347            menus[menu.id] = menu;
1348            menu.on("beforehide", onBeforeHide);
1349            menu.on("hide", onHide);
1350            menu.on("beforeshow", onBeforeShow);
1351            menu.on("show", onShow);
1352            var g = menu.group;
1353            if(g && menu.events["checkchange"]){
1354                if(!groups[g]){
1355                    groups[g] = [];
1356                }
1357                groups[g].push(menu);
1358                menu.on("checkchange", onCheck);
1359            }
1360        },
1361
1362         /**
1363          * Returns a {@link Roo.menu.Menu} object
1364          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1365          * be used to generate and return a new Menu instance.
1366          */
1367        get : function(menu){
1368            if(typeof menu == "string"){ // menu id
1369                return menus[menu];
1370            }else if(menu.events){  // menu instance
1371                return menu;
1372            }
1373            /*else if(typeof menu.length == 'number'){ // array of menu items?
1374                return new Roo.bootstrap.Menu({items:menu});
1375            }else{ // otherwise, must be a config
1376                return new Roo.bootstrap.Menu(menu);
1377            }
1378            */
1379            return false;
1380        },
1381
1382        // private
1383        unregister : function(menu){
1384            delete menus[menu.id];
1385            menu.un("beforehide", onBeforeHide);
1386            menu.un("hide", onHide);
1387            menu.un("beforeshow", onBeforeShow);
1388            menu.un("show", onShow);
1389            var g = menu.group;
1390            if(g && menu.events["checkchange"]){
1391                groups[g].remove(menu);
1392                menu.un("checkchange", onCheck);
1393            }
1394        },
1395
1396        // private
1397        registerCheckable : function(menuItem){
1398            var g = menuItem.group;
1399            if(g){
1400                if(!groups[g]){
1401                    groups[g] = [];
1402                }
1403                groups[g].push(menuItem);
1404                menuItem.on("beforecheckchange", onBeforeCheck);
1405            }
1406        },
1407
1408        // private
1409        unregisterCheckable : function(menuItem){
1410            var g = menuItem.group;
1411            if(g){
1412                groups[g].remove(menuItem);
1413                menuItem.un("beforecheckchange", onBeforeCheck);
1414            }
1415        }
1416    };
1417 }();/*
1418  * - LGPL
1419  *
1420  * menu
1421  * 
1422  */
1423
1424 /**
1425  * @class Roo.bootstrap.Menu
1426  * @extends Roo.bootstrap.Component
1427  * Bootstrap Menu class - container for MenuItems
1428  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1429  * 
1430  * @constructor
1431  * Create a new Menu
1432  * @param {Object} config The config object
1433  */
1434
1435
1436 Roo.bootstrap.Menu = function(config){
1437     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1438     if (this.registerMenu) {
1439         Roo.bootstrap.MenuMgr.register(this);
1440     }
1441     this.addEvents({
1442         /**
1443          * @event beforeshow
1444          * Fires before this menu is displayed
1445          * @param {Roo.menu.Menu} this
1446          */
1447         beforeshow : true,
1448         /**
1449          * @event beforehide
1450          * Fires before this menu is hidden
1451          * @param {Roo.menu.Menu} this
1452          */
1453         beforehide : true,
1454         /**
1455          * @event show
1456          * Fires after this menu is displayed
1457          * @param {Roo.menu.Menu} this
1458          */
1459         show : true,
1460         /**
1461          * @event hide
1462          * Fires after this menu is hidden
1463          * @param {Roo.menu.Menu} this
1464          */
1465         hide : true,
1466         /**
1467          * @event click
1468          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1469          * @param {Roo.menu.Menu} this
1470          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1471          * @param {Roo.EventObject} e
1472          */
1473         click : true,
1474         /**
1475          * @event mouseover
1476          * Fires when the mouse is hovering over this menu
1477          * @param {Roo.menu.Menu} this
1478          * @param {Roo.EventObject} e
1479          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1480          */
1481         mouseover : true,
1482         /**
1483          * @event mouseout
1484          * Fires when the mouse exits this menu
1485          * @param {Roo.menu.Menu} this
1486          * @param {Roo.EventObject} e
1487          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1488          */
1489         mouseout : true,
1490         /**
1491          * @event itemclick
1492          * Fires when a menu item contained in this menu is clicked
1493          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1494          * @param {Roo.EventObject} e
1495          */
1496         itemclick: true
1497     });
1498     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1499 };
1500
1501 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1502     
1503    /// html : false,
1504     //align : '',
1505     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1506     type: false,
1507     /**
1508      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1509      */
1510     registerMenu : true,
1511     
1512     menuItems :false, // stores the menu items..
1513     
1514     hidden:true,
1515     
1516     parentMenu : false,
1517     
1518     getChildContainer : function() {
1519         return this.el;  
1520     },
1521     
1522     getAutoCreate : function(){
1523          
1524         //if (['right'].indexOf(this.align)!==-1) {
1525         //    cfg.cn[1].cls += ' pull-right'
1526         //}
1527         
1528         
1529         var cfg = {
1530             tag : 'ul',
1531             cls : 'dropdown-menu' ,
1532             style : 'z-index:1000'
1533             
1534         }
1535         
1536         if (this.type === 'submenu') {
1537             cfg.cls = 'submenu active';
1538         }
1539         if (this.type === 'treeview') {
1540             cfg.cls = 'treeview-menu';
1541         }
1542         
1543         return cfg;
1544     },
1545     initEvents : function() {
1546         
1547        // Roo.log("ADD event");
1548        // Roo.log(this.triggerEl.dom);
1549         this.triggerEl.on('click', this.onTriggerPress, this);
1550         this.triggerEl.addClass('dropdown-toggle');
1551         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1552
1553         this.el.on("mouseover", this.onMouseOver, this);
1554         this.el.on("mouseout", this.onMouseOut, this);
1555         
1556         
1557     },
1558     findTargetItem : function(e){
1559         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1560         if(!t){
1561             return false;
1562         }
1563         //Roo.log(t);         Roo.log(t.id);
1564         if(t && t.id){
1565             //Roo.log(this.menuitems);
1566             return this.menuitems.get(t.id);
1567             
1568             //return this.items.get(t.menuItemId);
1569         }
1570         
1571         return false;
1572     },
1573     onClick : function(e){
1574         Roo.log("menu.onClick");
1575         var t = this.findTargetItem(e);
1576         if(!t){
1577             return;
1578         }
1579         Roo.log(e);
1580         /*
1581         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1582             if(t == this.activeItem && t.shouldDeactivate(e)){
1583                 this.activeItem.deactivate();
1584                 delete this.activeItem;
1585                 return;
1586             }
1587             if(t.canActivate){
1588                 this.setActiveItem(t, true);
1589             }
1590             return;
1591             
1592             
1593         }
1594         */
1595         Roo.log('pass click event');
1596         
1597         t.onClick(e);
1598         
1599         this.fireEvent("click", this, t, e);
1600         
1601         this.hide();
1602     },
1603      onMouseOver : function(e){
1604         var t  = this.findTargetItem(e);
1605         //Roo.log(t);
1606         //if(t){
1607         //    if(t.canActivate && !t.disabled){
1608         //        this.setActiveItem(t, true);
1609         //    }
1610         //}
1611         
1612         this.fireEvent("mouseover", this, e, t);
1613     },
1614     isVisible : function(){
1615         return !this.hidden;
1616     },
1617      onMouseOut : function(e){
1618         var t  = this.findTargetItem(e);
1619         
1620         //if(t ){
1621         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1622         //        this.activeItem.deactivate();
1623         //        delete this.activeItem;
1624         //    }
1625         //}
1626         this.fireEvent("mouseout", this, e, t);
1627     },
1628     
1629     
1630     /**
1631      * Displays this menu relative to another element
1632      * @param {String/HTMLElement/Roo.Element} element The element to align to
1633      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1634      * the element (defaults to this.defaultAlign)
1635      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1636      */
1637     show : function(el, pos, parentMenu){
1638         this.parentMenu = parentMenu;
1639         if(!this.el){
1640             this.render();
1641         }
1642         this.fireEvent("beforeshow", this);
1643         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1644     },
1645      /**
1646      * Displays this menu at a specific xy position
1647      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1648      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1649      */
1650     showAt : function(xy, parentMenu, /* private: */_e){
1651         this.parentMenu = parentMenu;
1652         if(!this.el){
1653             this.render();
1654         }
1655         if(_e !== false){
1656             this.fireEvent("beforeshow", this);
1657             
1658             //xy = this.el.adjustForConstraints(xy);
1659         }
1660         //this.el.setXY(xy);
1661         //this.el.show();
1662         this.hideMenuItems();
1663         this.hidden = false;
1664         this.triggerEl.addClass('open');
1665         this.focus();
1666         this.fireEvent("show", this);
1667     },
1668     
1669     focus : function(){
1670         return;
1671         if(!this.hidden){
1672             this.doFocus.defer(50, this);
1673         }
1674     },
1675
1676     doFocus : function(){
1677         if(!this.hidden){
1678             this.focusEl.focus();
1679         }
1680     },
1681
1682     /**
1683      * Hides this menu and optionally all parent menus
1684      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1685      */
1686     hide : function(deep){
1687         
1688         this.hideMenuItems();
1689         if(this.el && this.isVisible()){
1690             this.fireEvent("beforehide", this);
1691             if(this.activeItem){
1692                 this.activeItem.deactivate();
1693                 this.activeItem = null;
1694             }
1695             this.triggerEl.removeClass('open');;
1696             this.hidden = true;
1697             this.fireEvent("hide", this);
1698         }
1699         if(deep === true && this.parentMenu){
1700             this.parentMenu.hide(true);
1701         }
1702     },
1703     
1704     onTriggerPress  : function(e)
1705     {
1706         
1707         Roo.log('trigger press');
1708         //Roo.log(e.getTarget());
1709        // Roo.log(this.triggerEl.dom);
1710         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1711             return;
1712         }
1713         if (this.isVisible()) {
1714             Roo.log('hide');
1715             this.hide();
1716         } else {
1717             this.show(this.triggerEl, false, false);
1718         }
1719         
1720         
1721     },
1722     
1723          
1724        
1725     
1726     hideMenuItems : function()
1727     {
1728         //$(backdrop).remove()
1729         Roo.select('.open',true).each(function(aa) {
1730             
1731             aa.removeClass('open');
1732           //var parent = getParent($(this))
1733           //var relatedTarget = { relatedTarget: this }
1734           
1735            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1736           //if (e.isDefaultPrevented()) return
1737            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1738         })
1739     },
1740     addxtypeChild : function (tree, cntr) {
1741         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1742           
1743         this.menuitems.add(comp);
1744         return comp;
1745
1746     },
1747     getEl : function()
1748     {
1749         Roo.log(this.el);
1750         return this.el;
1751     }
1752 });
1753
1754  
1755  /*
1756  * - LGPL
1757  *
1758  * menu item
1759  * 
1760  */
1761
1762
1763 /**
1764  * @class Roo.bootstrap.MenuItem
1765  * @extends Roo.bootstrap.Component
1766  * Bootstrap MenuItem class
1767  * @cfg {String} html the menu label
1768  * @cfg {String} href the link
1769  * @cfg {Boolean} preventDefault (true | false) default true
1770  * 
1771  * 
1772  * @constructor
1773  * Create a new MenuItem
1774  * @param {Object} config The config object
1775  */
1776
1777
1778 Roo.bootstrap.MenuItem = function(config){
1779     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1780     this.addEvents({
1781         // raw events
1782         /**
1783          * @event click
1784          * The raw click event for the entire grid.
1785          * @param {Roo.EventObject} e
1786          */
1787         "click" : true
1788     });
1789 };
1790
1791 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1792     
1793     href : false,
1794     html : false,
1795     preventDefault: true,
1796     
1797     getAutoCreate : function(){
1798         var cfg= {
1799             tag: 'li',
1800             cls: 'dropdown-menu-item',
1801             cn: [
1802                     {
1803                         tag : 'a',
1804                         href : '#',
1805                         html : 'Link'
1806                     }
1807                 ]
1808         };
1809         if (this.parent().type == 'treeview') {
1810             cfg.cls = 'treeview-menu';
1811         }
1812         
1813         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1814         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1815         return cfg;
1816     },
1817     
1818     initEvents: function() {
1819         
1820         //this.el.select('a').on('click', this.onClick, this);
1821         
1822     },
1823     onClick : function(e)
1824     {
1825         Roo.log('item on click ');
1826         //if(this.preventDefault){
1827         //    e.preventDefault();
1828         //}
1829         //this.parent().hideMenuItems();
1830         
1831         this.fireEvent('click', this, e);
1832     },
1833     getEl : function()
1834     {
1835         return this.el;
1836     }
1837 });
1838
1839  
1840
1841  /*
1842  * - LGPL
1843  *
1844  * menu separator
1845  * 
1846  */
1847
1848
1849 /**
1850  * @class Roo.bootstrap.MenuSeparator
1851  * @extends Roo.bootstrap.Component
1852  * Bootstrap MenuSeparator class
1853  * 
1854  * @constructor
1855  * Create a new MenuItem
1856  * @param {Object} config The config object
1857  */
1858
1859
1860 Roo.bootstrap.MenuSeparator = function(config){
1861     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1862 };
1863
1864 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1865     
1866     getAutoCreate : function(){
1867         var cfg = {
1868             cls: 'divider',
1869             tag : 'li'
1870         };
1871         
1872         return cfg;
1873     }
1874    
1875 });
1876
1877  
1878
1879  
1880 /*
1881 <div class="modal fade">
1882   <div class="modal-dialog">
1883     <div class="modal-content">
1884       <div class="modal-header">
1885         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1886         <h4 class="modal-title">Modal title</h4>
1887       </div>
1888       <div class="modal-body">
1889         <p>One fine body&hellip;</p>
1890       </div>
1891       <div class="modal-footer">
1892         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1893         <button type="button" class="btn btn-primary">Save changes</button>
1894       </div>
1895     </div><!-- /.modal-content -->
1896   </div><!-- /.modal-dialog -->
1897 </div><!-- /.modal -->
1898 */
1899 /*
1900  * - LGPL
1901  *
1902  * page contgainer.
1903  * 
1904  */
1905
1906 /**
1907  * @class Roo.bootstrap.Modal
1908  * @extends Roo.bootstrap.Component
1909  * Bootstrap Modal class
1910  * @cfg {String} title Title of dialog
1911  * @cfg {Boolean} specificTitle (true|false) default false
1912  * @cfg {Array} buttons Array of buttons or standard button set..
1913  * @cfg {String} buttonPosition (left|right|center) default right
1914  * 
1915  * @constructor
1916  * Create a new Modal Dialog
1917  * @param {Object} config The config object
1918  */
1919
1920 Roo.bootstrap.Modal = function(config){
1921     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1922     this.addEvents({
1923         // raw events
1924         /**
1925          * @event btnclick
1926          * The raw btnclick event for the button
1927          * @param {Roo.EventObject} e
1928          */
1929         "btnclick" : true
1930     });
1931     this.buttons = this.buttons || [];
1932 };
1933
1934 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1935     
1936     title : 'test dialog',
1937    
1938     buttons : false,
1939     
1940     // set on load...
1941     body:  false,
1942     
1943     specificTitle: false,
1944     
1945     buttonPosition: 'right',
1946     
1947     onRender : function(ct, position)
1948     {
1949         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1950      
1951         if(!this.el){
1952             var cfg = Roo.apply({},  this.getAutoCreate());
1953             cfg.id = Roo.id();
1954             //if(!cfg.name){
1955             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1956             //}
1957             //if (!cfg.name.length) {
1958             //    delete cfg.name;
1959            // }
1960             if (this.cls) {
1961                 cfg.cls += ' ' + this.cls;
1962             }
1963             if (this.style) {
1964                 cfg.style = this.style;
1965             }
1966             this.el = Roo.get(document.body).createChild(cfg, position);
1967         }
1968         //var type = this.el.dom.type;
1969         
1970         if(this.tabIndex !== undefined){
1971             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1972         }
1973         
1974         
1975         
1976         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1977         this.maskEl.enableDisplayMode("block");
1978         this.maskEl.hide();
1979         //this.el.addClass("x-dlg-modal");
1980     
1981         if (this.buttons.length) {
1982             Roo.each(this.buttons, function(bb) {
1983                 b = Roo.apply({}, bb);
1984                 b.xns = b.xns || Roo.bootstrap;
1985                 b.xtype = b.xtype || 'Button';
1986                 if (typeof(b.listeners) == 'undefined') {
1987                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1988                 }
1989                 
1990                 var btn = Roo.factory(b);
1991                 
1992                 btn.onRender(this.el.select('.modal-footer div').first());
1993                 
1994             },this);
1995         }
1996         // render the children.
1997         var nitems = [];
1998         
1999         if(typeof(this.items) != 'undefined'){
2000             var items = this.items;
2001             delete this.items;
2002
2003             for(var i =0;i < items.length;i++) {
2004                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2005             }
2006         }
2007         
2008         this.items = nitems;
2009         
2010         this.body = this.el.select('.modal-body',true).first();
2011         this.close = this.el.select('.modal-header .close', true).first();
2012         this.footer = this.el.select('.modal-footer',true).first();
2013         this.initEvents();
2014         //this.el.addClass([this.fieldClass, this.cls]);
2015         
2016     },
2017     getAutoCreate : function(){
2018         
2019         
2020         var bdy = {
2021                 cls : 'modal-body',
2022                 html : this.html || ''
2023         };
2024         
2025         var title = {
2026             tag: 'h4',
2027             cls : 'modal-title',
2028             html : this.title
2029         };
2030         
2031         if(this.specificTitle){
2032             title = this.title;
2033         };
2034         
2035         return modal = {
2036             cls: "modal fade",
2037             style : 'display: none',
2038             cn : [
2039                 {
2040                     cls: "modal-dialog",
2041                     cn : [
2042                         {
2043                             cls : "modal-content",
2044                             cn : [
2045                                 {
2046                                     cls : 'modal-header',
2047                                     cn : [
2048                                         {
2049                                             tag: 'button',
2050                                             cls : 'close',
2051                                             html : '&times'
2052                                         },
2053                                         title
2054                                     ]
2055                                 },
2056                                 bdy,
2057                                 {
2058                                     cls : 'modal-footer',
2059                                     cn : [
2060                                         {
2061                                             tag: 'div',
2062                                             cls: 'btn-' + this.buttonPosition
2063                                         }
2064                                     ]
2065                                     
2066                                 }
2067                                 
2068                                 
2069                             ]
2070                             
2071                         }
2072                     ]
2073                         
2074                 }
2075             ]
2076             
2077             
2078         };
2079           
2080     },
2081     getChildContainer : function() {
2082          
2083          return this.el.select('.modal-body',true).first();
2084         
2085     },
2086     getButtonContainer : function() {
2087          return this.el.select('.modal-footer div',true).first();
2088         
2089     },
2090     initEvents : function()
2091     {
2092         this.el.select('.modal-header .close').on('click', this.hide, this);
2093 //        
2094 //        this.addxtype(this);
2095     },
2096     show : function() {
2097         
2098         if (!this.rendered) {
2099             this.render();
2100         }
2101        
2102         this.el.addClass('on');
2103         this.el.removeClass('fade');
2104         this.el.setStyle('display', 'block');
2105         Roo.get(document.body).addClass("x-body-masked");
2106         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2107         this.maskEl.show();
2108         this.el.setStyle('zIndex', '10001');
2109         this.fireEvent('show', this);
2110         
2111         
2112     },
2113     hide : function()
2114     {
2115         Roo.log('Modal hide?!');
2116         this.maskEl.hide();
2117         Roo.get(document.body).removeClass("x-body-masked");
2118         this.el.removeClass('on');
2119         this.el.addClass('fade');
2120         this.el.setStyle('display', 'none');
2121         this.fireEvent('hide', this);
2122     },
2123     
2124     addButton : function(str, cb)
2125     {
2126          
2127         
2128         var b = Roo.apply({}, { html : str } );
2129         b.xns = b.xns || Roo.bootstrap;
2130         b.xtype = b.xtype || 'Button';
2131         if (typeof(b.listeners) == 'undefined') {
2132             b.listeners = { click : cb.createDelegate(this)  };
2133         }
2134         
2135         var btn = Roo.factory(b);
2136            
2137         btn.onRender(this.el.select('.modal-footer div').first());
2138         
2139         return btn;   
2140        
2141     },
2142     
2143     setDefaultButton : function(btn)
2144     {
2145         //this.el.select('.modal-footer').()
2146     },
2147     resizeTo: function(w,h)
2148     {
2149         // skip..
2150     },
2151     setContentSize  : function(w, h)
2152     {
2153         
2154     },
2155     onButtonClick: function(btn,e)
2156     {
2157         //Roo.log([a,b,c]);
2158         this.fireEvent('btnclick', btn.name, e);
2159     },
2160     setTitle: function(str) {
2161         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2162         
2163     }
2164 });
2165
2166
2167 Roo.apply(Roo.bootstrap.Modal,  {
2168     /**
2169          * Button config that displays a single OK button
2170          * @type Object
2171          */
2172         OK :  [{
2173             name : 'ok',
2174             weight : 'primary',
2175             html : 'OK'
2176         }], 
2177         /**
2178          * Button config that displays Yes and No buttons
2179          * @type Object
2180          */
2181         YESNO : [
2182             {
2183                 name  : 'no',
2184                 html : 'No'
2185             },
2186             {
2187                 name  :'yes',
2188                 weight : 'primary',
2189                 html : 'Yes'
2190             }
2191         ],
2192         
2193         /**
2194          * Button config that displays OK and Cancel buttons
2195          * @type Object
2196          */
2197         OKCANCEL : [
2198             {
2199                name : 'cancel',
2200                 html : 'Cancel'
2201             },
2202             {
2203                 name : 'ok',
2204                 weight : 'primary',
2205                 html : 'OK'
2206             }
2207         ],
2208         /**
2209          * Button config that displays Yes, No and Cancel buttons
2210          * @type Object
2211          */
2212         YESNOCANCEL : [
2213             {
2214                 name : 'yes',
2215                 weight : 'primary',
2216                 html : 'Yes'
2217             },
2218             {
2219                 name : 'no',
2220                 html : 'No'
2221             },
2222             {
2223                 name : 'cancel',
2224                 html : 'Cancel'
2225             }
2226         ]
2227 });
2228  /*
2229  * - LGPL
2230  *
2231  * messagebox - can be used as a replace
2232  * 
2233  */
2234 /**
2235  * @class Roo.MessageBox
2236  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2237  * Example usage:
2238  *<pre><code>
2239 // Basic alert:
2240 Roo.Msg.alert('Status', 'Changes saved successfully.');
2241
2242 // Prompt for user data:
2243 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2244     if (btn == 'ok'){
2245         // process text value...
2246     }
2247 });
2248
2249 // Show a dialog using config options:
2250 Roo.Msg.show({
2251    title:'Save Changes?',
2252    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2253    buttons: Roo.Msg.YESNOCANCEL,
2254    fn: processResult,
2255    animEl: 'elId'
2256 });
2257 </code></pre>
2258  * @singleton
2259  */
2260 Roo.bootstrap.MessageBox = function(){
2261     var dlg, opt, mask, waitTimer;
2262     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2263     var buttons, activeTextEl, bwidth;
2264
2265     
2266     // private
2267     var handleButton = function(button){
2268         dlg.hide();
2269         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2270     };
2271
2272     // private
2273     var handleHide = function(){
2274         if(opt && opt.cls){
2275             dlg.el.removeClass(opt.cls);
2276         }
2277         //if(waitTimer){
2278         //    Roo.TaskMgr.stop(waitTimer);
2279         //    waitTimer = null;
2280         //}
2281     };
2282
2283     // private
2284     var updateButtons = function(b){
2285         var width = 0;
2286         if(!b){
2287             buttons["ok"].hide();
2288             buttons["cancel"].hide();
2289             buttons["yes"].hide();
2290             buttons["no"].hide();
2291             //dlg.footer.dom.style.display = 'none';
2292             return width;
2293         }
2294         dlg.footer.dom.style.display = '';
2295         for(var k in buttons){
2296             if(typeof buttons[k] != "function"){
2297                 if(b[k]){
2298                     buttons[k].show();
2299                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2300                     width += buttons[k].el.getWidth()+15;
2301                 }else{
2302                     buttons[k].hide();
2303                 }
2304             }
2305         }
2306         return width;
2307     };
2308
2309     // private
2310     var handleEsc = function(d, k, e){
2311         if(opt && opt.closable !== false){
2312             dlg.hide();
2313         }
2314         if(e){
2315             e.stopEvent();
2316         }
2317     };
2318
2319     return {
2320         /**
2321          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2322          * @return {Roo.BasicDialog} The BasicDialog element
2323          */
2324         getDialog : function(){
2325            if(!dlg){
2326                 dlg = new Roo.bootstrap.Modal( {
2327                     //draggable: true,
2328                     //resizable:false,
2329                     //constraintoviewport:false,
2330                     //fixedcenter:true,
2331                     //collapsible : false,
2332                     //shim:true,
2333                     //modal: true,
2334                   //  width:400,
2335                   //  height:100,
2336                     //buttonAlign:"center",
2337                     closeClick : function(){
2338                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2339                             handleButton("no");
2340                         }else{
2341                             handleButton("cancel");
2342                         }
2343                     }
2344                 });
2345                 dlg.render();
2346                 dlg.on("hide", handleHide);
2347                 mask = dlg.mask;
2348                 //dlg.addKeyListener(27, handleEsc);
2349                 buttons = {};
2350                 this.buttons = buttons;
2351                 var bt = this.buttonText;
2352                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2353                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2354                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2355                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2356                 Roo.log(buttons)
2357                 bodyEl = dlg.body.createChild({
2358
2359                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2360                         '<textarea class="roo-mb-textarea"></textarea>' +
2361                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2362                 });
2363                 msgEl = bodyEl.dom.firstChild;
2364                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2365                 textboxEl.enableDisplayMode();
2366                 textboxEl.addKeyListener([10,13], function(){
2367                     if(dlg.isVisible() && opt && opt.buttons){
2368                         if(opt.buttons.ok){
2369                             handleButton("ok");
2370                         }else if(opt.buttons.yes){
2371                             handleButton("yes");
2372                         }
2373                     }
2374                 });
2375                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2376                 textareaEl.enableDisplayMode();
2377                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2378                 progressEl.enableDisplayMode();
2379                 var pf = progressEl.dom.firstChild;
2380                 if (pf) {
2381                     pp = Roo.get(pf.firstChild);
2382                     pp.setHeight(pf.offsetHeight);
2383                 }
2384                 
2385             }
2386             return dlg;
2387         },
2388
2389         /**
2390          * Updates the message box body text
2391          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2392          * the XHTML-compliant non-breaking space character '&amp;#160;')
2393          * @return {Roo.MessageBox} This message box
2394          */
2395         updateText : function(text){
2396             if(!dlg.isVisible() && !opt.width){
2397                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2398             }
2399             msgEl.innerHTML = text || '&#160;';
2400       
2401             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2402             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2403             var w = Math.max(
2404                     Math.min(opt.width || cw , this.maxWidth), 
2405                     Math.max(opt.minWidth || this.minWidth, bwidth)
2406             );
2407             if(opt.prompt){
2408                 activeTextEl.setWidth(w);
2409             }
2410             if(dlg.isVisible()){
2411                 dlg.fixedcenter = false;
2412             }
2413             // to big, make it scroll. = But as usual stupid IE does not support
2414             // !important..
2415             
2416             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2417                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2418                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2419             } else {
2420                 bodyEl.dom.style.height = '';
2421                 bodyEl.dom.style.overflowY = '';
2422             }
2423             if (cw > w) {
2424                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2425             } else {
2426                 bodyEl.dom.style.overflowX = '';
2427             }
2428             
2429             dlg.setContentSize(w, bodyEl.getHeight());
2430             if(dlg.isVisible()){
2431                 dlg.fixedcenter = true;
2432             }
2433             return this;
2434         },
2435
2436         /**
2437          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2438          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2439          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2440          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2441          * @return {Roo.MessageBox} This message box
2442          */
2443         updateProgress : function(value, text){
2444             if(text){
2445                 this.updateText(text);
2446             }
2447             if (pp) { // weird bug on my firefox - for some reason this is not defined
2448                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2449             }
2450             return this;
2451         },        
2452
2453         /**
2454          * Returns true if the message box is currently displayed
2455          * @return {Boolean} True if the message box is visible, else false
2456          */
2457         isVisible : function(){
2458             return dlg && dlg.isVisible();  
2459         },
2460
2461         /**
2462          * Hides the message box if it is displayed
2463          */
2464         hide : function(){
2465             if(this.isVisible()){
2466                 dlg.hide();
2467             }  
2468         },
2469
2470         /**
2471          * Displays a new message box, or reinitializes an existing message box, based on the config options
2472          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2473          * The following config object properties are supported:
2474          * <pre>
2475 Property    Type             Description
2476 ----------  ---------------  ------------------------------------------------------------------------------------
2477 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2478                                    closes (defaults to undefined)
2479 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2480                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2481 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2482                                    progress and wait dialogs will ignore this property and always hide the
2483                                    close button as they can only be closed programmatically.
2484 cls               String           A custom CSS class to apply to the message box element
2485 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2486                                    displayed (defaults to 75)
2487 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2488                                    function will be btn (the name of the button that was clicked, if applicable,
2489                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2490                                    Progress and wait dialogs will ignore this option since they do not respond to
2491                                    user actions and can only be closed programmatically, so any required function
2492                                    should be called by the same code after it closes the dialog.
2493 icon              String           A CSS class that provides a background image to be used as an icon for
2494                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2495 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2496 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2497 modal             Boolean          False to allow user interaction with the page while the message box is
2498                                    displayed (defaults to true)
2499 msg               String           A string that will replace the existing message box body text (defaults
2500                                    to the XHTML-compliant non-breaking space character '&#160;')
2501 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2502 progress          Boolean          True to display a progress bar (defaults to false)
2503 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2504 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2505 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2506 title             String           The title text
2507 value             String           The string value to set into the active textbox element if displayed
2508 wait              Boolean          True to display a progress bar (defaults to false)
2509 width             Number           The width of the dialog in pixels
2510 </pre>
2511          *
2512          * Example usage:
2513          * <pre><code>
2514 Roo.Msg.show({
2515    title: 'Address',
2516    msg: 'Please enter your address:',
2517    width: 300,
2518    buttons: Roo.MessageBox.OKCANCEL,
2519    multiline: true,
2520    fn: saveAddress,
2521    animEl: 'addAddressBtn'
2522 });
2523 </code></pre>
2524          * @param {Object} config Configuration options
2525          * @return {Roo.MessageBox} This message box
2526          */
2527         show : function(options)
2528         {
2529             
2530             // this causes nightmares if you show one dialog after another
2531             // especially on callbacks..
2532              
2533             if(this.isVisible()){
2534                 
2535                 this.hide();
2536                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2537                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2538                 Roo.log("New Dialog Message:" +  options.msg )
2539                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2540                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2541                 
2542             }
2543             var d = this.getDialog();
2544             opt = options;
2545             d.setTitle(opt.title || "&#160;");
2546             d.close.setDisplayed(opt.closable !== false);
2547             activeTextEl = textboxEl;
2548             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2549             if(opt.prompt){
2550                 if(opt.multiline){
2551                     textboxEl.hide();
2552                     textareaEl.show();
2553                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2554                         opt.multiline : this.defaultTextHeight);
2555                     activeTextEl = textareaEl;
2556                 }else{
2557                     textboxEl.show();
2558                     textareaEl.hide();
2559                 }
2560             }else{
2561                 textboxEl.hide();
2562                 textareaEl.hide();
2563             }
2564             progressEl.setDisplayed(opt.progress === true);
2565             this.updateProgress(0);
2566             activeTextEl.dom.value = opt.value || "";
2567             if(opt.prompt){
2568                 dlg.setDefaultButton(activeTextEl);
2569             }else{
2570                 var bs = opt.buttons;
2571                 var db = null;
2572                 if(bs && bs.ok){
2573                     db = buttons["ok"];
2574                 }else if(bs && bs.yes){
2575                     db = buttons["yes"];
2576                 }
2577                 dlg.setDefaultButton(db);
2578             }
2579             bwidth = updateButtons(opt.buttons);
2580             this.updateText(opt.msg);
2581             if(opt.cls){
2582                 d.el.addClass(opt.cls);
2583             }
2584             d.proxyDrag = opt.proxyDrag === true;
2585             d.modal = opt.modal !== false;
2586             d.mask = opt.modal !== false ? mask : false;
2587             if(!d.isVisible()){
2588                 // force it to the end of the z-index stack so it gets a cursor in FF
2589                 document.body.appendChild(dlg.el.dom);
2590                 d.animateTarget = null;
2591                 d.show(options.animEl);
2592             }
2593             return this;
2594         },
2595
2596         /**
2597          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2598          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2599          * and closing the message box when the process is complete.
2600          * @param {String} title The title bar text
2601          * @param {String} msg The message box body text
2602          * @return {Roo.MessageBox} This message box
2603          */
2604         progress : function(title, msg){
2605             this.show({
2606                 title : title,
2607                 msg : msg,
2608                 buttons: false,
2609                 progress:true,
2610                 closable:false,
2611                 minWidth: this.minProgressWidth,
2612                 modal : true
2613             });
2614             return this;
2615         },
2616
2617         /**
2618          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2619          * If a callback function is passed it will be called after the user clicks the button, and the
2620          * id of the button that was clicked will be passed as the only parameter to the callback
2621          * (could also be the top-right close button).
2622          * @param {String} title The title bar text
2623          * @param {String} msg The message box body text
2624          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2625          * @param {Object} scope (optional) The scope of the callback function
2626          * @return {Roo.MessageBox} This message box
2627          */
2628         alert : function(title, msg, fn, scope){
2629             this.show({
2630                 title : title,
2631                 msg : msg,
2632                 buttons: this.OK,
2633                 fn: fn,
2634                 scope : scope,
2635                 modal : true
2636             });
2637             return this;
2638         },
2639
2640         /**
2641          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2642          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2643          * You are responsible for closing the message box when the process is complete.
2644          * @param {String} msg The message box body text
2645          * @param {String} title (optional) The title bar text
2646          * @return {Roo.MessageBox} This message box
2647          */
2648         wait : function(msg, title){
2649             this.show({
2650                 title : title,
2651                 msg : msg,
2652                 buttons: false,
2653                 closable:false,
2654                 progress:true,
2655                 modal:true,
2656                 width:300,
2657                 wait:true
2658             });
2659             waitTimer = Roo.TaskMgr.start({
2660                 run: function(i){
2661                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2662                 },
2663                 interval: 1000
2664             });
2665             return this;
2666         },
2667
2668         /**
2669          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2670          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2671          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2672          * @param {String} title The title bar text
2673          * @param {String} msg The message box body text
2674          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2675          * @param {Object} scope (optional) The scope of the callback function
2676          * @return {Roo.MessageBox} This message box
2677          */
2678         confirm : function(title, msg, fn, scope){
2679             this.show({
2680                 title : title,
2681                 msg : msg,
2682                 buttons: this.YESNO,
2683                 fn: fn,
2684                 scope : scope,
2685                 modal : true
2686             });
2687             return this;
2688         },
2689
2690         /**
2691          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2692          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2693          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2694          * (could also be the top-right close button) and the text that was entered will be passed as the two
2695          * parameters to the callback.
2696          * @param {String} title The title bar text
2697          * @param {String} msg The message box body text
2698          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2699          * @param {Object} scope (optional) The scope of the callback function
2700          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2701          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2702          * @return {Roo.MessageBox} This message box
2703          */
2704         prompt : function(title, msg, fn, scope, multiline){
2705             this.show({
2706                 title : title,
2707                 msg : msg,
2708                 buttons: this.OKCANCEL,
2709                 fn: fn,
2710                 minWidth:250,
2711                 scope : scope,
2712                 prompt:true,
2713                 multiline: multiline,
2714                 modal : true
2715             });
2716             return this;
2717         },
2718
2719         /**
2720          * Button config that displays a single OK button
2721          * @type Object
2722          */
2723         OK : {ok:true},
2724         /**
2725          * Button config that displays Yes and No buttons
2726          * @type Object
2727          */
2728         YESNO : {yes:true, no:true},
2729         /**
2730          * Button config that displays OK and Cancel buttons
2731          * @type Object
2732          */
2733         OKCANCEL : {ok:true, cancel:true},
2734         /**
2735          * Button config that displays Yes, No and Cancel buttons
2736          * @type Object
2737          */
2738         YESNOCANCEL : {yes:true, no:true, cancel:true},
2739
2740         /**
2741          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2742          * @type Number
2743          */
2744         defaultTextHeight : 75,
2745         /**
2746          * The maximum width in pixels of the message box (defaults to 600)
2747          * @type Number
2748          */
2749         maxWidth : 600,
2750         /**
2751          * The minimum width in pixels of the message box (defaults to 100)
2752          * @type Number
2753          */
2754         minWidth : 100,
2755         /**
2756          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2757          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2758          * @type Number
2759          */
2760         minProgressWidth : 250,
2761         /**
2762          * An object containing the default button text strings that can be overriden for localized language support.
2763          * Supported properties are: ok, cancel, yes and no.
2764          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2765          * @type Object
2766          */
2767         buttonText : {
2768             ok : "OK",
2769             cancel : "Cancel",
2770             yes : "Yes",
2771             no : "No"
2772         }
2773     };
2774 }();
2775
2776 /**
2777  * Shorthand for {@link Roo.MessageBox}
2778  */
2779 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2780 Roo.Msg = Roo.Msg || Roo.MessageBox;
2781 /*
2782  * - LGPL
2783  *
2784  * navbar
2785  * 
2786  */
2787
2788 /**
2789  * @class Roo.bootstrap.Navbar
2790  * @extends Roo.bootstrap.Component
2791  * Bootstrap Navbar class
2792
2793  * @constructor
2794  * Create a new Navbar
2795  * @param {Object} config The config object
2796  */
2797
2798
2799 Roo.bootstrap.Navbar = function(config){
2800     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2801     
2802 };
2803
2804 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2805     
2806     
2807    
2808     // private
2809     navItems : false,
2810     loadMask : false,
2811     
2812     
2813     getAutoCreate : function(){
2814         
2815         
2816         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2817         
2818     },
2819     
2820     initEvents :function ()
2821     {
2822         //Roo.log(this.el.select('.navbar-toggle',true));
2823         this.el.select('.navbar-toggle',true).on('click', function() {
2824            // Roo.log('click');
2825             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2826         }, this);
2827         
2828         var mark = {
2829             tag: "div",
2830             cls:"x-dlg-mask"
2831         }
2832         
2833         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2834         
2835         var size = this.el.getSize();
2836         this.maskEl.setSize(size.width, size.height);
2837         this.maskEl.enableDisplayMode("block");
2838         this.maskEl.hide();
2839         
2840         if(this.loadMask){
2841             this.maskEl.show();
2842         }
2843     },
2844     
2845     
2846     getChildContainer : function()
2847     {
2848         if (this.el.select('.collapse').getCount()) {
2849             return this.el.select('.collapse',true).first();
2850         }
2851         
2852         return this.el;
2853     },
2854     
2855     mask : function()
2856     {
2857         this.maskEl.show();
2858     },
2859     
2860     unmask : function()
2861     {
2862         this.maskEl.hide();
2863     } 
2864     
2865     
2866     
2867     
2868 });
2869
2870
2871
2872  
2873
2874  /*
2875  * - LGPL
2876  *
2877  * navbar
2878  * 
2879  */
2880
2881 /**
2882  * @class Roo.bootstrap.NavSimplebar
2883  * @extends Roo.bootstrap.Navbar
2884  * Bootstrap Sidebar class
2885  *
2886  * @cfg {Boolean} inverse is inverted color
2887  * 
2888  * @cfg {String} type (nav | pills | tabs)
2889  * @cfg {Boolean} arrangement stacked | justified
2890  * @cfg {String} align (left | right) alignment
2891  * 
2892  * @cfg {Boolean} main (true|false) main nav bar? default false
2893  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2894  * 
2895  * @cfg {String} tag (header|footer|nav|div) default is nav 
2896
2897  * 
2898  * 
2899  * 
2900  * @constructor
2901  * Create a new Sidebar
2902  * @param {Object} config The config object
2903  */
2904
2905
2906 Roo.bootstrap.NavSimplebar = function(config){
2907     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2908 };
2909
2910 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2911     
2912     inverse: false,
2913     
2914     type: false,
2915     arrangement: '',
2916     align : false,
2917     
2918     
2919     
2920     main : false,
2921     
2922     
2923     tag : false,
2924     
2925     
2926     getAutoCreate : function(){
2927         
2928         
2929         var cfg = {
2930             tag : this.tag || 'div',
2931             cls : 'navbar'
2932         };
2933           
2934         
2935         cfg.cn = [
2936             {
2937                 cls: 'nav',
2938                 tag : 'ul'
2939             }
2940         ];
2941         
2942          
2943         this.type = this.type || 'nav';
2944         if (['tabs','pills'].indexOf(this.type)!==-1) {
2945             cfg.cn[0].cls += ' nav-' + this.type
2946         
2947         
2948         } else {
2949             if (this.type!=='nav') {
2950                 Roo.log('nav type must be nav/tabs/pills')
2951             }
2952             cfg.cn[0].cls += ' navbar-nav'
2953         }
2954         
2955         
2956         
2957         
2958         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2959             cfg.cn[0].cls += ' nav-' + this.arrangement;
2960         }
2961         
2962         
2963         if (this.align === 'right') {
2964             cfg.cn[0].cls += ' navbar-right';
2965         }
2966         
2967         if (this.inverse) {
2968             cfg.cls += ' navbar-inverse';
2969             
2970         }
2971         
2972         
2973         return cfg;
2974     
2975         
2976     }
2977     
2978     
2979     
2980 });
2981
2982
2983
2984  
2985
2986  
2987        /*
2988  * - LGPL
2989  *
2990  * navbar
2991  * 
2992  */
2993
2994 /**
2995  * @class Roo.bootstrap.NavHeaderbar
2996  * @extends Roo.bootstrap.NavSimplebar
2997  * Bootstrap Sidebar class
2998  *
2999  * @cfg {String} brand what is brand
3000  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3001  * @cfg {String} brand_href href of the brand
3002  * 
3003  * @constructor
3004  * Create a new Sidebar
3005  * @param {Object} config The config object
3006  */
3007
3008
3009 Roo.bootstrap.NavHeaderbar = function(config){
3010     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3011 };
3012
3013 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3014     
3015     position: '',
3016     brand: '',
3017     brand_href: false,
3018     
3019     
3020     getAutoCreate : function(){
3021         
3022         
3023         
3024         var   cfg = {
3025             tag: this.nav || 'nav',
3026             cls: 'navbar',
3027             role: 'navigation',
3028             cn: [
3029                 {
3030                     tag: 'div',
3031                     cls: 'navbar-header',
3032                     cn: [
3033                         {
3034                         tag: 'button',
3035                         type: 'button',
3036                         cls: 'navbar-toggle',
3037                         'data-toggle': 'collapse',
3038                         cn: [
3039                             {
3040                                 tag: 'span',
3041                                 cls: 'sr-only',
3042                                 html: 'Toggle navigation'
3043                             },
3044                             {
3045                                 tag: 'span',
3046                                 cls: 'icon-bar'
3047                             },
3048                             {
3049                                 tag: 'span',
3050                                 cls: 'icon-bar'
3051                             },
3052                             {
3053                                 tag: 'span',
3054                                 cls: 'icon-bar'
3055                             }
3056                         ]
3057                         }
3058                     ]
3059                 },
3060                 {
3061                 tag: 'div',
3062                 cls: 'collapse navbar-collapse'
3063                 }
3064             ]
3065         };
3066         
3067         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3068         
3069         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3070             cfg.cls += ' navbar-' + this.position;
3071             
3072             // tag can override this..
3073             
3074             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3075         }
3076         
3077         if (this.brand !== '') {
3078             cfg.cn[0].cn.push({
3079                 tag: 'a',
3080                 href: this.brand_href ? this.brand_href : '#',
3081                 cls: 'navbar-brand',
3082                 cn: [
3083                 this.brand
3084                 ]
3085             });
3086         }
3087         
3088         if(this.main){
3089             cfg.cls += ' main-nav';
3090         }
3091         
3092         
3093         return cfg;
3094
3095         
3096     }
3097     
3098     
3099     
3100 });
3101
3102
3103
3104  
3105
3106  /*
3107  * - LGPL
3108  *
3109  * navbar
3110  * 
3111  */
3112
3113 /**
3114  * @class Roo.bootstrap.NavSidebar
3115  * @extends Roo.bootstrap.Navbar
3116  * Bootstrap Sidebar class
3117  * 
3118  * @constructor
3119  * Create a new Sidebar
3120  * @param {Object} config The config object
3121  */
3122
3123
3124 Roo.bootstrap.NavSidebar = function(config){
3125     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3126 };
3127
3128 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3129     
3130     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3131     
3132     getAutoCreate : function(){
3133         
3134         
3135         return  {
3136             tag: 'div',
3137             cls: 'sidebar sidebar-nav'
3138         };
3139     
3140         
3141     }
3142     
3143     
3144     
3145 });
3146
3147
3148
3149  
3150
3151  /*
3152  * - LGPL
3153  *
3154  * nav group
3155  * 
3156  */
3157
3158 /**
3159  * @class Roo.bootstrap.NavGroup
3160  * @extends Roo.bootstrap.Component
3161  * Bootstrap NavGroup class
3162  * @cfg {String} align left | right
3163  * @cfg {Boolean} inverse false | true
3164  * @cfg {String} type (nav|pills|tab) default nav
3165  * @cfg {String} navId - reference Id for navbar.
3166
3167  * 
3168  * @constructor
3169  * Create a new nav group
3170  * @param {Object} config The config object
3171  */
3172
3173 Roo.bootstrap.NavGroup = function(config){
3174     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3175     this.navItems = [];
3176     Roo.bootstrap.NavGroup.register(this);
3177      this.addEvents({
3178         /**
3179              * @event changed
3180              * Fires when the active item changes
3181              * @param {Roo.bootstrap.NavGroup} this
3182              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3183              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3184          */
3185         'changed': true
3186      });
3187     
3188 };
3189
3190 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3191     
3192     align: '',
3193     inverse: false,
3194     form: false,
3195     type: 'nav',
3196     navId : '',
3197     // private
3198     
3199     navItems : false,
3200     
3201     getAutoCreate : function()
3202     {
3203         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3204         
3205         cfg = {
3206             tag : 'ul',
3207             cls: 'nav' 
3208         }
3209         
3210         if (['tabs','pills'].indexOf(this.type)!==-1) {
3211             cfg.cls += ' nav-' + this.type
3212         } else {
3213             if (this.type!=='nav') {
3214                 Roo.log('nav type must be nav/tabs/pills')
3215             }
3216             cfg.cls += ' navbar-nav'
3217         }
3218         
3219         if (this.parent().sidebar) {
3220             cfg = {
3221                 tag: 'ul',
3222                 cls: 'dashboard-menu sidebar-menu'
3223             }
3224             
3225             return cfg;
3226         }
3227         
3228         if (this.form === true) {
3229             cfg = {
3230                 tag: 'form',
3231                 cls: 'navbar-form'
3232             }
3233             
3234             if (this.align === 'right') {
3235                 cfg.cls += ' navbar-right';
3236             } else {
3237                 cfg.cls += ' navbar-left';
3238             }
3239         }
3240         
3241         if (this.align === 'right') {
3242             cfg.cls += ' navbar-right';
3243         }
3244         
3245         if (this.inverse) {
3246             cfg.cls += ' navbar-inverse';
3247             
3248         }
3249         
3250         
3251         return cfg;
3252     },
3253     
3254     setActiveItem : function(item)
3255     {
3256         var prev = false;
3257         Roo.each(this.navItems, function(v){
3258             if (v == item) {
3259                 return ;
3260             }
3261             if (v.isActive()) {
3262                 v.setActive(false, true);
3263                 prev = v;
3264                 
3265             }
3266             
3267         });
3268
3269         item.setActive(true, true);
3270         this.fireEvent('changed', this, item, prev);
3271         
3272         
3273     },
3274     
3275     addItem : function(cfg)
3276     {
3277         var cn = new Roo.bootstrap.NavItem(cfg);
3278         this.register(cn);
3279         cn.parentId = this.id;
3280         cn.onRender(this.el, null);
3281         return cn;
3282     },
3283     
3284     register : function(item)
3285     {
3286         this.navItems.push( item);
3287         item.navId = this.navId;
3288     
3289     },
3290     getNavItem: function(tabId)
3291     {
3292         var ret = false;
3293         Roo.each(this.navItems, function(e) {
3294             if (e.tabId == tabId) {
3295                ret =  e;
3296                return false;
3297             }
3298             return true;
3299             
3300         });
3301         return ret;
3302     }
3303     
3304     
3305     
3306     
3307 });
3308
3309  
3310 Roo.apply(Roo.bootstrap.NavGroup, {
3311     
3312     groups: {},
3313     
3314     register : function(navgrp)
3315     {
3316         this.groups[navgrp.navId] = navgrp;
3317         
3318     },
3319     get: function(navId) {
3320         return this.groups[navId];
3321     }
3322     
3323     
3324     
3325 });
3326
3327  /*
3328  * - LGPL
3329  *
3330  * row
3331  * 
3332  */
3333
3334 /**
3335  * @class Roo.bootstrap.NavItem
3336  * @extends Roo.bootstrap.Component
3337  * Bootstrap Navbar.NavItem class
3338  * @cfg {String} href  link to
3339  * @cfg {String} html content of button
3340  * @cfg {String} badge text inside badge
3341  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3342  * @cfg {String} glyphicon name of glyphicon
3343  * @cfg {String} icon name of font awesome icon
3344  * @cfg {Boolean} active Is item active
3345  * @cfg {Boolean} disabled Is item disabled
3346  
3347  * @cfg {Boolean} preventDefault (true | false) default false
3348  * @cfg {String} tabId the tab that this item activates.
3349  * @cfg {String} tagtype (a|span) render as a href or span?
3350   
3351  * @constructor
3352  * Create a new Navbar Item
3353  * @param {Object} config The config object
3354  */
3355 Roo.bootstrap.NavItem = function(config){
3356     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3357     this.addEvents({
3358         // raw events
3359         /**
3360          * @event click
3361          * The raw click event for the entire grid.
3362          * @param {Roo.EventObject} e
3363          */
3364         "click" : true,
3365          /**
3366             * @event changed
3367             * Fires when the active item active state changes
3368             * @param {Roo.bootstrap.NavItem} this
3369             * @param {boolean} state the new state
3370              
3371          */
3372         'changed': true
3373     });
3374    
3375 };
3376
3377 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3378     
3379     href: false,
3380     html: '',
3381     badge: '',
3382     icon: false,
3383     glyphicon: false,
3384     active: false,
3385     preventDefault : false,
3386     tabId : false,
3387     tagtype : 'a',
3388     disabled : false,
3389     
3390     getAutoCreate : function(){
3391          
3392         var cfg = {
3393             tag: 'li',
3394             cls: 'nav-item'
3395             
3396         }
3397         if (this.active) {
3398             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3399         }
3400         if (this.disabled) {
3401             cfg.cls += ' disabled';
3402         }
3403         
3404         if (this.href || this.html) {
3405             cfg.cn = [
3406                 {
3407                     tag: this.tagtype,
3408                     href : this.href || "#",
3409                     html: this.html || ''
3410                 }
3411             ]
3412         
3413         // glyphicon and icon go before content..
3414             if (this.glyphicon || this.icon) {
3415                  if (this.icon) {
3416                     cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3417                 } else {
3418                     cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3419                 }
3420             }
3421             
3422             
3423             
3424             if (cfg.cn  && this.menu) {
3425                 
3426                 cfg.cn[0].html += " <span class='caret'></span>";
3427              
3428             }
3429             
3430             if (this.badge !== '') {
3431                  
3432                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3433             }
3434         }
3435         
3436         
3437         
3438         return cfg;
3439     },
3440     initEvents: function() {
3441        // Roo.log('init events?');
3442        // Roo.log(this.el.dom);
3443         if (typeof (this.menu) != 'undefined') {
3444             this.menu.parentType = this.xtype;
3445             this.menu.triggerEl = this.el;
3446             this.addxtype(Roo.apply({}, this.menu));
3447         }
3448
3449        
3450         this.el.select('a',true).on('click', this.onClick, this);
3451         // at this point parent should be available..
3452         this.parent().register(this);
3453     },
3454     
3455     onClick : function(e)
3456     {
3457          
3458         if(this.preventDefault){
3459             e.preventDefault();
3460         }
3461         if (this.disabled) {
3462             return;
3463         }
3464         Roo.log("fire event clicked");
3465         if(this.fireEvent('click', this, e) === false){
3466             return;
3467         };
3468         
3469         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3470             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3471                 this.parent().setActiveItem(this);
3472             }
3473             
3474             
3475             
3476         } 
3477     },
3478     
3479     isActive: function () {
3480         return this.active
3481     },
3482     setActive : function(state, fire)
3483     {
3484         this.active = state;
3485         if (!state ) {
3486             this.el.removeClass('active');
3487         } else if (!this.el.hasClass('active')) {
3488             this.el.addClass('active');
3489         }
3490         if (fire) {
3491             this.fireEvent('changed', this, state);
3492         }
3493         
3494         
3495     },
3496      // this should not be here...
3497     setDisabled : function(state)
3498     {
3499         this.disabled = state;
3500         if (!state ) {
3501             this.el.removeClass('disabled');
3502         } else if (!this.el.hasClass('disabled')) {
3503             this.el.addClass('disabled');
3504         }
3505         
3506     }
3507 });
3508  
3509
3510  /*
3511  * - LGPL
3512  *
3513  * sidebar item
3514  *
3515  *  li
3516  *    <span> icon </span>
3517  *    <span> text </span>
3518  *    <span>badge </span>
3519  */
3520
3521 /**
3522  * @class Roo.bootstrap.NavSidebarItem
3523  * @extends Roo.bootstrap.NavItem
3524  * Bootstrap Navbar.NavSidebarItem class
3525  * @constructor
3526  * Create a new Navbar Button
3527  * @param {Object} config The config object
3528  */
3529 Roo.bootstrap.NavSidebarItem = function(config){
3530     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3531     this.addEvents({
3532         // raw events
3533         /**
3534          * @event click
3535          * The raw click event for the entire grid.
3536          * @param {Roo.EventObject} e
3537          */
3538         "click" : true,
3539          /**
3540             * @event changed
3541             * Fires when the active item active state changes
3542             * @param {Roo.bootstrap.NavSidebarItem} this
3543             * @param {boolean} state the new state
3544              
3545          */
3546         'changed': true
3547     });
3548    
3549 };
3550
3551 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3552     
3553     
3554     getAutoCreate : function(){
3555         
3556         
3557         var a = {
3558                 tag: 'a',
3559                 href : this.href || '#',
3560                 cls: '',
3561                 html : '',
3562                 cn : []
3563         };
3564         var cfg = {
3565             tag: 'li',
3566             cls: '',
3567             cn: [ a ]
3568         }
3569         var span = {
3570             tag: 'span',
3571             html : this.html || ''
3572         }
3573         
3574         
3575         if (this.active) {
3576             cfg.cls += ' active';
3577         }
3578         
3579         // left icon..
3580         if (this.glyphicon || this.icon) {
3581             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3582             a.cn.push({ tag : 'i', cls : c }) ;
3583         }
3584         // html..
3585         a.cn.push(span);
3586         // then badge..
3587         if (this.badge !== '') {
3588             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3589         }
3590         // fi
3591         if (this.menu) {
3592             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3593             a.cls += 'dropdown-toggle treeview' ;
3594             
3595         }
3596         
3597         
3598         
3599         return cfg;
3600          
3601            
3602     }
3603    
3604      
3605  
3606 });
3607  
3608
3609  /*
3610  * - LGPL
3611  *
3612  * row
3613  * 
3614  */
3615
3616 /**
3617  * @class Roo.bootstrap.Row
3618  * @extends Roo.bootstrap.Component
3619  * Bootstrap Row class (contains columns...)
3620  * 
3621  * @constructor
3622  * Create a new Row
3623  * @param {Object} config The config object
3624  */
3625
3626 Roo.bootstrap.Row = function(config){
3627     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3628 };
3629
3630 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3631     
3632     getAutoCreate : function(){
3633        return {
3634             cls: 'row clearfix'
3635        };
3636     }
3637     
3638     
3639 });
3640
3641  
3642
3643  /*
3644  * - LGPL
3645  *
3646  * element
3647  * 
3648  */
3649
3650 /**
3651  * @class Roo.bootstrap.Element
3652  * @extends Roo.bootstrap.Component
3653  * Bootstrap Element class
3654  * @cfg {String} html contents of the element
3655  * @cfg {String} tag tag of the element
3656  * @cfg {String} cls class of the element
3657  * 
3658  * @constructor
3659  * Create a new Element
3660  * @param {Object} config The config object
3661  */
3662
3663 Roo.bootstrap.Element = function(config){
3664     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3665 };
3666
3667 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3668     
3669     tag: 'div',
3670     cls: '',
3671     html: '',
3672      
3673     
3674     getAutoCreate : function(){
3675         
3676         var cfg = {
3677             tag: this.tag,
3678             cls: this.cls,
3679             html: this.html
3680         }
3681         
3682         
3683         
3684         return cfg;
3685     }
3686    
3687 });
3688
3689  
3690
3691  /*
3692  * - LGPL
3693  *
3694  * pagination
3695  * 
3696  */
3697
3698 /**
3699  * @class Roo.bootstrap.Pagination
3700  * @extends Roo.bootstrap.Component
3701  * Bootstrap Pagination class
3702  * @cfg {String} size xs | sm | md | lg
3703  * @cfg {Boolean} inverse false | true
3704  * 
3705  * @constructor
3706  * Create a new Pagination
3707  * @param {Object} config The config object
3708  */
3709
3710 Roo.bootstrap.Pagination = function(config){
3711     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3712 };
3713
3714 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3715     
3716     cls: false,
3717     size: false,
3718     inverse: false,
3719     
3720     getAutoCreate : function(){
3721         var cfg = {
3722             tag: 'ul',
3723                 cls: 'pagination'
3724         };
3725         if (this.inverse) {
3726             cfg.cls += ' inverse';
3727         }
3728         if (this.html) {
3729             cfg.html=this.html;
3730         }
3731         if (this.cls) {
3732             cfg.cls += " " + this.cls;
3733         }
3734         return cfg;
3735     }
3736    
3737 });
3738
3739  
3740
3741  /*
3742  * - LGPL
3743  *
3744  * Pagination item
3745  * 
3746  */
3747
3748
3749 /**
3750  * @class Roo.bootstrap.PaginationItem
3751  * @extends Roo.bootstrap.Component
3752  * Bootstrap PaginationItem class
3753  * @cfg {String} html text
3754  * @cfg {String} href the link
3755  * @cfg {Boolean} preventDefault (true | false) default true
3756  * @cfg {Boolean} active (true | false) default false
3757  * 
3758  * 
3759  * @constructor
3760  * Create a new PaginationItem
3761  * @param {Object} config The config object
3762  */
3763
3764
3765 Roo.bootstrap.PaginationItem = function(config){
3766     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3767     this.addEvents({
3768         // raw events
3769         /**
3770          * @event click
3771          * The raw click event for the entire grid.
3772          * @param {Roo.EventObject} e
3773          */
3774         "click" : true
3775     });
3776 };
3777
3778 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3779     
3780     href : false,
3781     html : false,
3782     preventDefault: true,
3783     active : false,
3784     cls : false,
3785     
3786     getAutoCreate : function(){
3787         var cfg= {
3788             tag: 'li',
3789             cn: [
3790                 {
3791                     tag : 'a',
3792                     href : this.href ? this.href : '#',
3793                     html : this.html ? this.html : ''
3794                 }
3795             ]
3796         };
3797         
3798         if(this.cls){
3799             cfg.cls = this.cls;
3800         }
3801         
3802         if(this.active){
3803             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3804         }
3805         
3806         return cfg;
3807     },
3808     
3809     initEvents: function() {
3810         
3811         this.el.on('click', this.onClick, this);
3812         
3813     },
3814     onClick : function(e)
3815     {
3816         Roo.log('PaginationItem on click ');
3817         if(this.preventDefault){
3818             e.preventDefault();
3819         }
3820         
3821         this.fireEvent('click', this, e);
3822     }
3823    
3824 });
3825
3826  
3827
3828  /*
3829  * - LGPL
3830  *
3831  * slider
3832  * 
3833  */
3834
3835
3836 /**
3837  * @class Roo.bootstrap.Slider
3838  * @extends Roo.bootstrap.Component
3839  * Bootstrap Slider class
3840  *    
3841  * @constructor
3842  * Create a new Slider
3843  * @param {Object} config The config object
3844  */
3845
3846 Roo.bootstrap.Slider = function(config){
3847     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3848 };
3849
3850 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3851     
3852     getAutoCreate : function(){
3853         
3854         var cfg = {
3855             tag: 'div',
3856             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3857             cn: [
3858                 {
3859                     tag: 'a',
3860                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3861                 }
3862             ]
3863         }
3864         
3865         return cfg;
3866     }
3867    
3868 });
3869
3870  /*
3871  * Based on:
3872  * Ext JS Library 1.1.1
3873  * Copyright(c) 2006-2007, Ext JS, LLC.
3874  *
3875  * Originally Released Under LGPL - original licence link has changed is not relivant.
3876  *
3877  * Fork - LGPL
3878  * <script type="text/javascript">
3879  */
3880  
3881
3882 /**
3883  * @class Roo.grid.ColumnModel
3884  * @extends Roo.util.Observable
3885  * This is the default implementation of a ColumnModel used by the Grid. It defines
3886  * the columns in the grid.
3887  * <br>Usage:<br>
3888  <pre><code>
3889  var colModel = new Roo.grid.ColumnModel([
3890         {header: "Ticker", width: 60, sortable: true, locked: true},
3891         {header: "Company Name", width: 150, sortable: true},
3892         {header: "Market Cap.", width: 100, sortable: true},
3893         {header: "$ Sales", width: 100, sortable: true, renderer: money},
3894         {header: "Employees", width: 100, sortable: true, resizable: false}
3895  ]);
3896  </code></pre>
3897  * <p>
3898  
3899  * The config options listed for this class are options which may appear in each
3900  * individual column definition.
3901  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
3902  * @constructor
3903  * @param {Object} config An Array of column config objects. See this class's
3904  * config objects for details.
3905 */
3906 Roo.grid.ColumnModel = function(config){
3907         /**
3908      * The config passed into the constructor
3909      */
3910     this.config = config;
3911     this.lookup = {};
3912
3913     // if no id, create one
3914     // if the column does not have a dataIndex mapping,
3915     // map it to the order it is in the config
3916     for(var i = 0, len = config.length; i < len; i++){
3917         var c = config[i];
3918         if(typeof c.dataIndex == "undefined"){
3919             c.dataIndex = i;
3920         }
3921         if(typeof c.renderer == "string"){
3922             c.renderer = Roo.util.Format[c.renderer];
3923         }
3924         if(typeof c.id == "undefined"){
3925             c.id = Roo.id();
3926         }
3927         if(c.editor && c.editor.xtype){
3928             c.editor  = Roo.factory(c.editor, Roo.grid);
3929         }
3930         if(c.editor && c.editor.isFormField){
3931             c.editor = new Roo.grid.GridEditor(c.editor);
3932         }
3933         this.lookup[c.id] = c;
3934     }
3935
3936     /**
3937      * The width of columns which have no width specified (defaults to 100)
3938      * @type Number
3939      */
3940     this.defaultWidth = 100;
3941
3942     /**
3943      * Default sortable of columns which have no sortable specified (defaults to false)
3944      * @type Boolean
3945      */
3946     this.defaultSortable = false;
3947
3948     this.addEvents({
3949         /**
3950              * @event widthchange
3951              * Fires when the width of a column changes.
3952              * @param {ColumnModel} this
3953              * @param {Number} columnIndex The column index
3954              * @param {Number} newWidth The new width
3955              */
3956             "widthchange": true,
3957         /**
3958              * @event headerchange
3959              * Fires when the text of a header changes.
3960              * @param {ColumnModel} this
3961              * @param {Number} columnIndex The column index
3962              * @param {Number} newText The new header text
3963              */
3964             "headerchange": true,
3965         /**
3966              * @event hiddenchange
3967              * Fires when a column is hidden or "unhidden".
3968              * @param {ColumnModel} this
3969              * @param {Number} columnIndex The column index
3970              * @param {Boolean} hidden true if hidden, false otherwise
3971              */
3972             "hiddenchange": true,
3973             /**
3974          * @event columnmoved
3975          * Fires when a column is moved.
3976          * @param {ColumnModel} this
3977          * @param {Number} oldIndex
3978          * @param {Number} newIndex
3979          */
3980         "columnmoved" : true,
3981         /**
3982          * @event columlockchange
3983          * Fires when a column's locked state is changed
3984          * @param {ColumnModel} this
3985          * @param {Number} colIndex
3986          * @param {Boolean} locked true if locked
3987          */
3988         "columnlockchange" : true
3989     });
3990     Roo.grid.ColumnModel.superclass.constructor.call(this);
3991 };
3992 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
3993     /**
3994      * @cfg {String} header The header text to display in the Grid view.
3995      */
3996     /**
3997      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
3998      * {@link Roo.data.Record} definition from which to draw the column's value. If not
3999      * specified, the column's index is used as an index into the Record's data Array.
4000      */
4001     /**
4002      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4003      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4004      */
4005     /**
4006      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4007      * Defaults to the value of the {@link #defaultSortable} property.
4008      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4009      */
4010     /**
4011      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4012      */
4013     /**
4014      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4015      */
4016     /**
4017      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4018      */
4019     /**
4020      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4021      */
4022     /**
4023      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4024      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4025      * default renderer uses the raw data value.
4026      */
4027        /**
4028      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4029      */
4030     /**
4031      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4032      */
4033
4034     /**
4035      * Returns the id of the column at the specified index.
4036      * @param {Number} index The column index
4037      * @return {String} the id
4038      */
4039     getColumnId : function(index){
4040         return this.config[index].id;
4041     },
4042
4043     /**
4044      * Returns the column for a specified id.
4045      * @param {String} id The column id
4046      * @return {Object} the column
4047      */
4048     getColumnById : function(id){
4049         return this.lookup[id];
4050     },
4051
4052     
4053     /**
4054      * Returns the column for a specified dataIndex.
4055      * @param {String} dataIndex The column dataIndex
4056      * @return {Object|Boolean} the column or false if not found
4057      */
4058     getColumnByDataIndex: function(dataIndex){
4059         var index = this.findColumnIndex(dataIndex);
4060         return index > -1 ? this.config[index] : false;
4061     },
4062     
4063     /**
4064      * Returns the index for a specified column id.
4065      * @param {String} id The column id
4066      * @return {Number} the index, or -1 if not found
4067      */
4068     getIndexById : function(id){
4069         for(var i = 0, len = this.config.length; i < len; i++){
4070             if(this.config[i].id == id){
4071                 return i;
4072             }
4073         }
4074         return -1;
4075     },
4076     
4077     /**
4078      * Returns the index for a specified column dataIndex.
4079      * @param {String} dataIndex The column dataIndex
4080      * @return {Number} the index, or -1 if not found
4081      */
4082     
4083     findColumnIndex : function(dataIndex){
4084         for(var i = 0, len = this.config.length; i < len; i++){
4085             if(this.config[i].dataIndex == dataIndex){
4086                 return i;
4087             }
4088         }
4089         return -1;
4090     },
4091     
4092     
4093     moveColumn : function(oldIndex, newIndex){
4094         var c = this.config[oldIndex];
4095         this.config.splice(oldIndex, 1);
4096         this.config.splice(newIndex, 0, c);
4097         this.dataMap = null;
4098         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4099     },
4100
4101     isLocked : function(colIndex){
4102         return this.config[colIndex].locked === true;
4103     },
4104
4105     setLocked : function(colIndex, value, suppressEvent){
4106         if(this.isLocked(colIndex) == value){
4107             return;
4108         }
4109         this.config[colIndex].locked = value;
4110         if(!suppressEvent){
4111             this.fireEvent("columnlockchange", this, colIndex, value);
4112         }
4113     },
4114
4115     getTotalLockedWidth : function(){
4116         var totalWidth = 0;
4117         for(var i = 0; i < this.config.length; i++){
4118             if(this.isLocked(i) && !this.isHidden(i)){
4119                 this.totalWidth += this.getColumnWidth(i);
4120             }
4121         }
4122         return totalWidth;
4123     },
4124
4125     getLockedCount : function(){
4126         for(var i = 0, len = this.config.length; i < len; i++){
4127             if(!this.isLocked(i)){
4128                 return i;
4129             }
4130         }
4131     },
4132
4133     /**
4134      * Returns the number of columns.
4135      * @return {Number}
4136      */
4137     getColumnCount : function(visibleOnly){
4138         if(visibleOnly === true){
4139             var c = 0;
4140             for(var i = 0, len = this.config.length; i < len; i++){
4141                 if(!this.isHidden(i)){
4142                     c++;
4143                 }
4144             }
4145             return c;
4146         }
4147         return this.config.length;
4148     },
4149
4150     /**
4151      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4152      * @param {Function} fn
4153      * @param {Object} scope (optional)
4154      * @return {Array} result
4155      */
4156     getColumnsBy : function(fn, scope){
4157         var r = [];
4158         for(var i = 0, len = this.config.length; i < len; i++){
4159             var c = this.config[i];
4160             if(fn.call(scope||this, c, i) === true){
4161                 r[r.length] = c;
4162             }
4163         }
4164         return r;
4165     },
4166
4167     /**
4168      * Returns true if the specified column is sortable.
4169      * @param {Number} col The column index
4170      * @return {Boolean}
4171      */
4172     isSortable : function(col){
4173         if(typeof this.config[col].sortable == "undefined"){
4174             return this.defaultSortable;
4175         }
4176         return this.config[col].sortable;
4177     },
4178
4179     /**
4180      * Returns the rendering (formatting) function defined for the column.
4181      * @param {Number} col The column index.
4182      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4183      */
4184     getRenderer : function(col){
4185         if(!this.config[col].renderer){
4186             return Roo.grid.ColumnModel.defaultRenderer;
4187         }
4188         return this.config[col].renderer;
4189     },
4190
4191     /**
4192      * Sets the rendering (formatting) function for a column.
4193      * @param {Number} col The column index
4194      * @param {Function} fn The function to use to process the cell's raw data
4195      * to return HTML markup for the grid view. The render function is called with
4196      * the following parameters:<ul>
4197      * <li>Data value.</li>
4198      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4199      * <li>css A CSS style string to apply to the table cell.</li>
4200      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4201      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4202      * <li>Row index</li>
4203      * <li>Column index</li>
4204      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4205      */
4206     setRenderer : function(col, fn){
4207         this.config[col].renderer = fn;
4208     },
4209
4210     /**
4211      * Returns the width for the specified column.
4212      * @param {Number} col The column index
4213      * @return {Number}
4214      */
4215     getColumnWidth : function(col){
4216         return this.config[col].width * 1 || this.defaultWidth;
4217     },
4218
4219     /**
4220      * Sets the width for a column.
4221      * @param {Number} col The column index
4222      * @param {Number} width The new width
4223      */
4224     setColumnWidth : function(col, width, suppressEvent){
4225         this.config[col].width = width;
4226         this.totalWidth = null;
4227         if(!suppressEvent){
4228              this.fireEvent("widthchange", this, col, width);
4229         }
4230     },
4231
4232     /**
4233      * Returns the total width of all columns.
4234      * @param {Boolean} includeHidden True to include hidden column widths
4235      * @return {Number}
4236      */
4237     getTotalWidth : function(includeHidden){
4238         if(!this.totalWidth){
4239             this.totalWidth = 0;
4240             for(var i = 0, len = this.config.length; i < len; i++){
4241                 if(includeHidden || !this.isHidden(i)){
4242                     this.totalWidth += this.getColumnWidth(i);
4243                 }
4244             }
4245         }
4246         return this.totalWidth;
4247     },
4248
4249     /**
4250      * Returns the header for the specified column.
4251      * @param {Number} col The column index
4252      * @return {String}
4253      */
4254     getColumnHeader : function(col){
4255         return this.config[col].header;
4256     },
4257
4258     /**
4259      * Sets the header for a column.
4260      * @param {Number} col The column index
4261      * @param {String} header The new header
4262      */
4263     setColumnHeader : function(col, header){
4264         this.config[col].header = header;
4265         this.fireEvent("headerchange", this, col, header);
4266     },
4267
4268     /**
4269      * Returns the tooltip for the specified column.
4270      * @param {Number} col The column index
4271      * @return {String}
4272      */
4273     getColumnTooltip : function(col){
4274             return this.config[col].tooltip;
4275     },
4276     /**
4277      * Sets the tooltip for a column.
4278      * @param {Number} col The column index
4279      * @param {String} tooltip The new tooltip
4280      */
4281     setColumnTooltip : function(col, tooltip){
4282             this.config[col].tooltip = tooltip;
4283     },
4284
4285     /**
4286      * Returns the dataIndex for the specified column.
4287      * @param {Number} col The column index
4288      * @return {Number}
4289      */
4290     getDataIndex : function(col){
4291         return this.config[col].dataIndex;
4292     },
4293
4294     /**
4295      * Sets the dataIndex for a column.
4296      * @param {Number} col The column index
4297      * @param {Number} dataIndex The new dataIndex
4298      */
4299     setDataIndex : function(col, dataIndex){
4300         this.config[col].dataIndex = dataIndex;
4301     },
4302
4303     
4304     
4305     /**
4306      * Returns true if the cell is editable.
4307      * @param {Number} colIndex The column index
4308      * @param {Number} rowIndex The row index
4309      * @return {Boolean}
4310      */
4311     isCellEditable : function(colIndex, rowIndex){
4312         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4313     },
4314
4315     /**
4316      * Returns the editor defined for the cell/column.
4317      * return false or null to disable editing.
4318      * @param {Number} colIndex The column index
4319      * @param {Number} rowIndex The row index
4320      * @return {Object}
4321      */
4322     getCellEditor : function(colIndex, rowIndex){
4323         return this.config[colIndex].editor;
4324     },
4325
4326     /**
4327      * Sets if a column is editable.
4328      * @param {Number} col The column index
4329      * @param {Boolean} editable True if the column is editable
4330      */
4331     setEditable : function(col, editable){
4332         this.config[col].editable = editable;
4333     },
4334
4335
4336     /**
4337      * Returns true if the column is hidden.
4338      * @param {Number} colIndex The column index
4339      * @return {Boolean}
4340      */
4341     isHidden : function(colIndex){
4342         return this.config[colIndex].hidden;
4343     },
4344
4345
4346     /**
4347      * Returns true if the column width cannot be changed
4348      */
4349     isFixed : function(colIndex){
4350         return this.config[colIndex].fixed;
4351     },
4352
4353     /**
4354      * Returns true if the column can be resized
4355      * @return {Boolean}
4356      */
4357     isResizable : function(colIndex){
4358         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4359     },
4360     /**
4361      * Sets if a column is hidden.
4362      * @param {Number} colIndex The column index
4363      * @param {Boolean} hidden True if the column is hidden
4364      */
4365     setHidden : function(colIndex, hidden){
4366         this.config[colIndex].hidden = hidden;
4367         this.totalWidth = null;
4368         this.fireEvent("hiddenchange", this, colIndex, hidden);
4369     },
4370
4371     /**
4372      * Sets the editor for a column.
4373      * @param {Number} col The column index
4374      * @param {Object} editor The editor object
4375      */
4376     setEditor : function(col, editor){
4377         this.config[col].editor = editor;
4378     }
4379 });
4380
4381 Roo.grid.ColumnModel.defaultRenderer = function(value){
4382         if(typeof value == "string" && value.length < 1){
4383             return "&#160;";
4384         }
4385         return value;
4386 };
4387
4388 // Alias for backwards compatibility
4389 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4390 /*
4391  * Based on:
4392  * Ext JS Library 1.1.1
4393  * Copyright(c) 2006-2007, Ext JS, LLC.
4394  *
4395  * Originally Released Under LGPL - original licence link has changed is not relivant.
4396  *
4397  * Fork - LGPL
4398  * <script type="text/javascript">
4399  */
4400  
4401 /**
4402  * @class Roo.LoadMask
4403  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4404  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4405  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4406  * element's UpdateManager load indicator and will be destroyed after the initial load.
4407  * @constructor
4408  * Create a new LoadMask
4409  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4410  * @param {Object} config The config object
4411  */
4412 Roo.LoadMask = function(el, config){
4413     this.el = Roo.get(el);
4414     Roo.apply(this, config);
4415     if(this.store){
4416         this.store.on('beforeload', this.onBeforeLoad, this);
4417         this.store.on('load', this.onLoad, this);
4418         this.store.on('loadexception', this.onLoadException, this);
4419         this.removeMask = false;
4420     }else{
4421         var um = this.el.getUpdateManager();
4422         um.showLoadIndicator = false; // disable the default indicator
4423         um.on('beforeupdate', this.onBeforeLoad, this);
4424         um.on('update', this.onLoad, this);
4425         um.on('failure', this.onLoad, this);
4426         this.removeMask = true;
4427     }
4428 };
4429
4430 Roo.LoadMask.prototype = {
4431     /**
4432      * @cfg {Boolean} removeMask
4433      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4434      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4435      */
4436     /**
4437      * @cfg {String} msg
4438      * The text to display in a centered loading message box (defaults to 'Loading...')
4439      */
4440     msg : 'Loading...',
4441     /**
4442      * @cfg {String} msgCls
4443      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4444      */
4445     msgCls : 'x-mask-loading',
4446
4447     /**
4448      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4449      * @type Boolean
4450      */
4451     disabled: false,
4452
4453     /**
4454      * Disables the mask to prevent it from being displayed
4455      */
4456     disable : function(){
4457        this.disabled = true;
4458     },
4459
4460     /**
4461      * Enables the mask so that it can be displayed
4462      */
4463     enable : function(){
4464         this.disabled = false;
4465     },
4466     
4467     onLoadException : function()
4468     {
4469         Roo.log(arguments);
4470         
4471         if (typeof(arguments[3]) != 'undefined') {
4472             Roo.MessageBox.alert("Error loading",arguments[3]);
4473         } 
4474         /*
4475         try {
4476             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4477                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4478             }   
4479         } catch(e) {
4480             
4481         }
4482         */
4483     
4484         
4485         
4486         this.el.unmask(this.removeMask);
4487     },
4488     // private
4489     onLoad : function()
4490     {
4491         this.el.unmask(this.removeMask);
4492     },
4493
4494     // private
4495     onBeforeLoad : function(){
4496         if(!this.disabled){
4497             this.el.mask(this.msg, this.msgCls);
4498         }
4499     },
4500
4501     // private
4502     destroy : function(){
4503         if(this.store){
4504             this.store.un('beforeload', this.onBeforeLoad, this);
4505             this.store.un('load', this.onLoad, this);
4506             this.store.un('loadexception', this.onLoadException, this);
4507         }else{
4508             var um = this.el.getUpdateManager();
4509             um.un('beforeupdate', this.onBeforeLoad, this);
4510             um.un('update', this.onLoad, this);
4511             um.un('failure', this.onLoad, this);
4512         }
4513     }
4514 };/*
4515  * - LGPL
4516  *
4517  * table
4518  * 
4519  */
4520
4521 /**
4522  * @class Roo.bootstrap.Table
4523  * @extends Roo.bootstrap.Component
4524  * Bootstrap Table class
4525  * @cfg {String} cls table class
4526  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4527  * @cfg {String} bgcolor Specifies the background color for a table
4528  * @cfg {Number} border Specifies whether the table cells should have borders or not
4529  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4530  * @cfg {Number} cellspacing Specifies the space between cells
4531  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4532  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4533  * @cfg {String} sortable Specifies that the table should be sortable
4534  * @cfg {String} summary Specifies a summary of the content of a table
4535  * @cfg {Number} width Specifies the width of a table
4536  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4537  * 
4538  * @cfg {boolean} striped Should the rows be alternative striped
4539  * @cfg {boolean} bordered Add borders to the table
4540  * @cfg {boolean} hover Add hover highlighting
4541  * @cfg {boolean} condensed Format condensed
4542  * @cfg {boolean} responsive Format condensed
4543  * @cfg {Boolean} loadMask (true|false) default false
4544  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4545  * @cfg {Boolean} thead (true|false) generate thead, default true
4546  * @cfg {Boolean} RowSelection (true|false) default false
4547  * @cfg {Boolean} CellSelection (true|false) default false
4548  *
4549  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4550  
4551  * 
4552  * @constructor
4553  * Create a new Table
4554  * @param {Object} config The config object
4555  */
4556
4557 Roo.bootstrap.Table = function(config){
4558     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4559     
4560     if (this.sm) {
4561         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4562         this.sm = this.selModel;
4563         this.sm.xmodule = this.xmodule || false;
4564     }
4565     if (this.cm && typeof(this.cm.config) == 'undefined') {
4566         this.colModel = new Roo.grid.ColumnModel(this.cm);
4567         this.cm = this.colModel;
4568         this.cm.xmodule = this.xmodule || false;
4569     }
4570     if (this.store) {
4571         this.store= Roo.factory(this.store, Roo.data);
4572         this.ds = this.store;
4573         this.ds.xmodule = this.xmodule || false;
4574          
4575     }
4576     if (this.footer && this.store) {
4577         this.footer.dataSource = this.ds;
4578         this.footer = Roo.factory(this.footer);
4579     }
4580     
4581     /** @private */
4582     this.addEvents({
4583         /**
4584          * @event cellclick
4585          * Fires when a cell is clicked
4586          * @param {Roo.bootstrap.Table} this
4587          * @param {Roo.Element} el
4588          * @param {Number} rowIndex
4589          * @param {Number} columnIndex
4590          * @param {Roo.EventObject} e
4591          */
4592         "cellclick" : true,
4593         /**
4594          * @event celldblclick
4595          * Fires when a cell is double clicked
4596          * @param {Roo.bootstrap.Table} this
4597          * @param {Roo.Element} el
4598          * @param {Number} rowIndex
4599          * @param {Number} columnIndex
4600          * @param {Roo.EventObject} e
4601          */
4602         "celldblclick" : true,
4603         /**
4604          * @event rowclick
4605          * Fires when a row is clicked
4606          * @param {Roo.bootstrap.Table} this
4607          * @param {Roo.Element} el
4608          * @param {Number} rowIndex
4609          * @param {Roo.EventObject} e
4610          */
4611         "rowclick" : true,
4612         /**
4613          * @event rowdblclick
4614          * Fires when a row is double clicked
4615          * @param {Roo.bootstrap.Table} this
4616          * @param {Roo.Element} el
4617          * @param {Number} rowIndex
4618          * @param {Roo.EventObject} e
4619          */
4620         "rowdblclick" : true,
4621         /**
4622          * @event rowclass
4623          * Fires when a row is rendered, so you can change add a style to it.
4624          * @param {Roo.bootstrap.Table} this
4625          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4626          */
4627         'rowclass' : true
4628         
4629     });
4630 };
4631
4632 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4633     
4634     cls: false,
4635     align: false,
4636     bgcolor: false,
4637     border: false,
4638     cellpadding: false,
4639     cellspacing: false,
4640     frame: false,
4641     rules: false,
4642     sortable: false,
4643     summary: false,
4644     width: false,
4645     striped : false,
4646     bordered: false,
4647     hover:  false,
4648     condensed : false,
4649     responsive : false,
4650     sm : false,
4651     cm : false,
4652     store : false,
4653     loadMask : false,
4654     tfoot : true,
4655     thead : true,
4656     RowSelection : false,
4657     CellSelection : false,
4658     layout : false,
4659     
4660     
4661     getAutoCreate : function(){
4662         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4663         
4664         cfg = {
4665             tag: 'table',
4666             cls : 'table',
4667             cn : []
4668         }
4669             
4670         if (this.striped) {
4671             cfg.cls += ' table-striped';
4672         }
4673         
4674         if (this.hover) {
4675             cfg.cls += ' table-hover';
4676         }
4677         if (this.bordered) {
4678             cfg.cls += ' table-bordered';
4679         }
4680         if (this.condensed) {
4681             cfg.cls += ' table-condensed';
4682         }
4683         if (this.responsive) {
4684             cfg.cls += ' table-responsive';
4685         }
4686         
4687         if (this.cls) {
4688             cfg.cls+=  ' ' +this.cls;
4689         }
4690         
4691         // this lot should be simplifed...
4692         
4693         if (this.align) {
4694             cfg.align=this.align;
4695         }
4696         if (this.bgcolor) {
4697             cfg.bgcolor=this.bgcolor;
4698         }
4699         if (this.border) {
4700             cfg.border=this.border;
4701         }
4702         if (this.cellpadding) {
4703             cfg.cellpadding=this.cellpadding;
4704         }
4705         if (this.cellspacing) {
4706             cfg.cellspacing=this.cellspacing;
4707         }
4708         if (this.frame) {
4709             cfg.frame=this.frame;
4710         }
4711         if (this.rules) {
4712             cfg.rules=this.rules;
4713         }
4714         if (this.sortable) {
4715             cfg.sortable=this.sortable;
4716         }
4717         if (this.summary) {
4718             cfg.summary=this.summary;
4719         }
4720         if (this.width) {
4721             cfg.width=this.width;
4722         }
4723         if (this.layout) {
4724             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4725         }
4726         
4727         if(this.store || this.cm){
4728             if(this.thead){
4729                 cfg.cn.push(this.renderHeader());
4730             }
4731             
4732             cfg.cn.push(this.renderBody());
4733             
4734             if(this.tfoot){
4735                 cfg.cn.push(this.renderFooter());
4736             }
4737             
4738             cfg.cls+=  ' TableGrid';
4739         }
4740         
4741         return { cn : [ cfg ] };
4742     },
4743     
4744     initEvents : function()
4745     {   
4746         if(!this.store || !this.cm){
4747             return;
4748         }
4749         
4750         Roo.log('initEvents with ds!!!!');
4751         
4752         var _this = this;
4753         
4754         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4755             e.on('click', _this.sort, _this);
4756         });
4757         
4758         this.el.on("click", this.onClick, this);
4759         this.el.on("dblclick", this.onDblClick, this);
4760         
4761         this.parent().el.setStyle('position', 'relative');
4762         if (this.footer) {
4763             this.footer.parentId = this.id;
4764             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
4765         }
4766         
4767         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
4768         
4769         this.store.on('load', this.onLoad, this);
4770         this.store.on('beforeload', this.onBeforeLoad, this);
4771         
4772     },
4773     
4774     onClick : function(e, el)
4775     {
4776         var cell = Roo.get(el);
4777         var row = cell.findParent('tr', false, true);
4778         var cellIndex = cell.dom.cellIndex;
4779         var rowIndex = row.dom.rowIndex;
4780         
4781         if(this.CellSelection){
4782             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
4783         }
4784         
4785         if(this.RowSelection){
4786             this.fireEvent('rowclick', this, row, rowIndex, e);
4787         }
4788         
4789         
4790     },
4791     
4792     onDblClick : function(e,el)
4793     {
4794         var cell = Roo.get(el);;
4795         var row = cell.findParent('tr', false, true);
4796         var cellIndex = cell.dom.cellIndex;
4797         var rowIndex = row.dom.rowIndex;
4798         
4799         if(this.CellSelection){
4800             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
4801         }
4802         
4803         if(this.RowSelection){
4804             this.fireEvent('rowdblclick', this, row, rowIndex, e);
4805         }
4806     },
4807     
4808     sort : function(e,el)
4809     {
4810         var col = Roo.get(el)
4811         
4812         if(!col.hasClass('sortable')){
4813             return;
4814         }
4815         
4816         var sort = col.attr('sort');
4817         var dir = 'ASC';
4818         
4819         if(col.hasClass('glyphicon-arrow-up')){
4820             dir = 'DESC';
4821         }
4822         
4823         this.store.sortInfo = {field : sort, direction : dir};
4824         
4825         if (this.footer) {
4826             Roo.log("calling footer first");
4827             this.footer.onClick('first');
4828         } else {
4829         
4830             this.store.load({ params : { start : 0 } });
4831         }
4832     },
4833     
4834     renderHeader : function()
4835     {
4836         var header = {
4837             tag: 'thead',
4838             cn : []
4839         };
4840         
4841         var cm = this.cm;
4842         
4843         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4844             
4845             var config = cm.config[i];
4846             
4847             if(typeof(config.hidden) != 'undefined' && config.hidden){
4848                 continue;
4849             }
4850                     
4851             var c = {
4852                 tag: 'th',
4853                 style : '',
4854                 html: cm.getColumnHeader(i)
4855             };
4856             
4857             if(typeof(config.dataIndex) != 'undefined'){
4858                 c.sort = config.dataIndex;
4859             }
4860             
4861             if(typeof(config.sortable) != 'undefined' && config.sortable){
4862                 c.cls = 'sortable';
4863             }
4864             
4865 //            if(typeof(config.align) != 'undefined' && config.align.length){
4866 //                c.style += ' text-align:' + config.align + ';';
4867 //            }
4868             
4869             if(typeof(config.width) != 'undefined'){
4870                 c.style += ' width:' + config.width + 'px;';
4871             }
4872             
4873             header.cn.push(c)
4874         }
4875         
4876         return header;
4877     },
4878     
4879     renderBody : function()
4880     {
4881         var body = {
4882             tag: 'tbody',
4883             cn : [
4884                 {
4885                     tag: 'tr',
4886                     cn : [
4887                         {
4888                             tag : 'td',
4889                             colspan :  this.cm.getColumnCount()
4890                         }
4891                     ]
4892                 }
4893             ]
4894         };
4895         
4896         return body;
4897     },
4898     
4899     renderFooter : function()
4900     {
4901         var footer = {
4902             tag: 'tfoot',
4903             cn : [
4904                 {
4905                     tag: 'tr',
4906                     cn : [
4907                         {
4908                             tag : 'td',
4909                             colspan :  this.cm.getColumnCount()
4910                         }
4911                     ]
4912                 }
4913             ]
4914         };
4915         
4916         return footer;
4917     },
4918     
4919     onLoad : function()
4920     {
4921         Roo.log('ds onload');
4922         this.clear();
4923         
4924         var _this = this;
4925         var cm = this.cm;
4926         
4927         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4928             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
4929             
4930             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
4931                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
4932             }
4933             
4934             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
4935                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
4936             }
4937         });
4938         
4939         var tbody = this.el.select('tbody', true).first();
4940         
4941         var renders = [];
4942                     
4943         if(this.store.getCount() > 0){
4944             this.store.data.each(function(d,rowIndex){
4945                 var row = {
4946                     tag : 'tr',
4947                     cn : []
4948                 };
4949                 
4950                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4951                     var config = cm.config[i];
4952                     
4953                     if(typeof(config.hidden) != 'undefined' && config.hidden){
4954                         continue;
4955                     }
4956                     
4957                     var renderer = cm.getRenderer(i);
4958                     var value = '';
4959                     var id = Roo.id();
4960                     
4961                     if(typeof(renderer) !== 'undefined'){
4962                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
4963                     }
4964                     
4965                     if(typeof(value) === 'object'){
4966                         renders.push({
4967                             container : id,
4968                             cfg : value 
4969                         })
4970                     }
4971                     
4972                     var rowcfg = {
4973                         record: d,
4974                         rowIndex : rowIndex,
4975                         colIndex : i,
4976                         rowClass : ''
4977                     }
4978
4979                     _this.fireEvent('rowclass', this, rowcfg);
4980                     
4981                     var td = {
4982                         tag: 'td',
4983                         id: id,
4984                         cls : rowcfg.rowClass,
4985                         style: '',
4986                         html: (typeof(value) === 'object') ? '' : value
4987                     };
4988                     
4989                     if(typeof(config.align) != 'undefined' && config.align.length){
4990                         td.style += ' text-align:' + config.align + ';';
4991                     }
4992                     
4993                     if(typeof(config.width) != 'undefined'){
4994                         td.style += ' width:' +  config.width + 'px;';
4995                     }
4996                     
4997                     
4998                     row.cn.push(td);
4999                    
5000                 }
5001                 
5002                 tbody.createChild(row);
5003                 
5004             });
5005         }
5006         
5007         
5008         if(renders.length){
5009             var _this = this;
5010             Roo.each(renders, function(r){
5011                 _this.renderColumn(r);
5012             })
5013         }
5014
5015         //if(this.loadMask){
5016         //    this.maskEl.hide();
5017         //}
5018     },
5019     
5020     onBeforeLoad : function()
5021     {
5022         //Roo.log('ds onBeforeLoad');
5023         
5024         //this.clear();
5025         
5026         //if(this.loadMask){
5027         //    this.maskEl.show();
5028         //}
5029     },
5030     
5031     clear : function()
5032     {
5033         this.el.select('tbody', true).first().dom.innerHTML = '';
5034     },
5035     
5036     getSelectionModel : function(){
5037         if(!this.selModel){
5038             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5039         }
5040         return this.selModel;
5041     },
5042     
5043     renderColumn : function(r)
5044     {
5045         var _this = this;
5046         
5047         var t = r.cfg.render(r.container);
5048         
5049         if(r.cfg.cn){
5050             Roo.each(r.cfg.cn, function(c){
5051                 var child = {
5052                     container: t.getChildContainer(),
5053                     cfg: c
5054                 }
5055                 _this.renderColumn(child);
5056             })
5057         }
5058     }
5059    
5060 });
5061
5062  
5063
5064  /*
5065  * - LGPL
5066  *
5067  * table cell
5068  * 
5069  */
5070
5071 /**
5072  * @class Roo.bootstrap.TableCell
5073  * @extends Roo.bootstrap.Component
5074  * Bootstrap TableCell class
5075  * @cfg {String} html cell contain text
5076  * @cfg {String} cls cell class
5077  * @cfg {String} tag cell tag (td|th) default td
5078  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5079  * @cfg {String} align Aligns the content in a cell
5080  * @cfg {String} axis Categorizes cells
5081  * @cfg {String} bgcolor Specifies the background color of a cell
5082  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5083  * @cfg {Number} colspan Specifies the number of columns a cell should span
5084  * @cfg {String} headers Specifies one or more header cells a cell is related to
5085  * @cfg {Number} height Sets the height of a cell
5086  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5087  * @cfg {Number} rowspan Sets the number of rows a cell should span
5088  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5089  * @cfg {String} valign Vertical aligns the content in a cell
5090  * @cfg {Number} width Specifies the width of a cell
5091  * 
5092  * @constructor
5093  * Create a new TableCell
5094  * @param {Object} config The config object
5095  */
5096
5097 Roo.bootstrap.TableCell = function(config){
5098     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5099 };
5100
5101 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5102     
5103     html: false,
5104     cls: false,
5105     tag: false,
5106     abbr: false,
5107     align: false,
5108     axis: false,
5109     bgcolor: false,
5110     charoff: false,
5111     colspan: false,
5112     headers: false,
5113     height: false,
5114     nowrap: false,
5115     rowspan: false,
5116     scope: false,
5117     valign: false,
5118     width: false,
5119     
5120     
5121     getAutoCreate : function(){
5122         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5123         
5124         cfg = {
5125             tag: 'td'
5126         }
5127         
5128         if(this.tag){
5129             cfg.tag = this.tag;
5130         }
5131         
5132         if (this.html) {
5133             cfg.html=this.html
5134         }
5135         if (this.cls) {
5136             cfg.cls=this.cls
5137         }
5138         if (this.abbr) {
5139             cfg.abbr=this.abbr
5140         }
5141         if (this.align) {
5142             cfg.align=this.align
5143         }
5144         if (this.axis) {
5145             cfg.axis=this.axis
5146         }
5147         if (this.bgcolor) {
5148             cfg.bgcolor=this.bgcolor
5149         }
5150         if (this.charoff) {
5151             cfg.charoff=this.charoff
5152         }
5153         if (this.colspan) {
5154             cfg.colspan=this.colspan
5155         }
5156         if (this.headers) {
5157             cfg.headers=this.headers
5158         }
5159         if (this.height) {
5160             cfg.height=this.height
5161         }
5162         if (this.nowrap) {
5163             cfg.nowrap=this.nowrap
5164         }
5165         if (this.rowspan) {
5166             cfg.rowspan=this.rowspan
5167         }
5168         if (this.scope) {
5169             cfg.scope=this.scope
5170         }
5171         if (this.valign) {
5172             cfg.valign=this.valign
5173         }
5174         if (this.width) {
5175             cfg.width=this.width
5176         }
5177         
5178         
5179         return cfg;
5180     }
5181    
5182 });
5183
5184  
5185
5186  /*
5187  * - LGPL
5188  *
5189  * table row
5190  * 
5191  */
5192
5193 /**
5194  * @class Roo.bootstrap.TableRow
5195  * @extends Roo.bootstrap.Component
5196  * Bootstrap TableRow class
5197  * @cfg {String} cls row class
5198  * @cfg {String} align Aligns the content in a table row
5199  * @cfg {String} bgcolor Specifies a background color for a table row
5200  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5201  * @cfg {String} valign Vertical aligns the content in a table row
5202  * 
5203  * @constructor
5204  * Create a new TableRow
5205  * @param {Object} config The config object
5206  */
5207
5208 Roo.bootstrap.TableRow = function(config){
5209     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5210 };
5211
5212 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5213     
5214     cls: false,
5215     align: false,
5216     bgcolor: false,
5217     charoff: false,
5218     valign: false,
5219     
5220     getAutoCreate : function(){
5221         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5222         
5223         cfg = {
5224             tag: 'tr'
5225         }
5226             
5227         if(this.cls){
5228             cfg.cls = this.cls;
5229         }
5230         if(this.align){
5231             cfg.align = this.align;
5232         }
5233         if(this.bgcolor){
5234             cfg.bgcolor = this.bgcolor;
5235         }
5236         if(this.charoff){
5237             cfg.charoff = this.charoff;
5238         }
5239         if(this.valign){
5240             cfg.valign = this.valign;
5241         }
5242         
5243         return cfg;
5244     }
5245    
5246 });
5247
5248  
5249
5250  /*
5251  * - LGPL
5252  *
5253  * table body
5254  * 
5255  */
5256
5257 /**
5258  * @class Roo.bootstrap.TableBody
5259  * @extends Roo.bootstrap.Component
5260  * Bootstrap TableBody class
5261  * @cfg {String} cls element class
5262  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5263  * @cfg {String} align Aligns the content inside the element
5264  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5265  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5266  * 
5267  * @constructor
5268  * Create a new TableBody
5269  * @param {Object} config The config object
5270  */
5271
5272 Roo.bootstrap.TableBody = function(config){
5273     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5274 };
5275
5276 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5277     
5278     cls: false,
5279     tag: false,
5280     align: false,
5281     charoff: false,
5282     valign: false,
5283     
5284     getAutoCreate : function(){
5285         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5286         
5287         cfg = {
5288             tag: 'tbody'
5289         }
5290             
5291         if (this.cls) {
5292             cfg.cls=this.cls
5293         }
5294         if(this.tag){
5295             cfg.tag = this.tag;
5296         }
5297         
5298         if(this.align){
5299             cfg.align = this.align;
5300         }
5301         if(this.charoff){
5302             cfg.charoff = this.charoff;
5303         }
5304         if(this.valign){
5305             cfg.valign = this.valign;
5306         }
5307         
5308         return cfg;
5309     }
5310     
5311     
5312 //    initEvents : function()
5313 //    {
5314 //        
5315 //        if(!this.store){
5316 //            return;
5317 //        }
5318 //        
5319 //        this.store = Roo.factory(this.store, Roo.data);
5320 //        this.store.on('load', this.onLoad, this);
5321 //        
5322 //        this.store.load();
5323 //        
5324 //    },
5325 //    
5326 //    onLoad: function () 
5327 //    {   
5328 //        this.fireEvent('load', this);
5329 //    }
5330 //    
5331 //   
5332 });
5333
5334  
5335
5336  /*
5337  * Based on:
5338  * Ext JS Library 1.1.1
5339  * Copyright(c) 2006-2007, Ext JS, LLC.
5340  *
5341  * Originally Released Under LGPL - original licence link has changed is not relivant.
5342  *
5343  * Fork - LGPL
5344  * <script type="text/javascript">
5345  */
5346
5347 // as we use this in bootstrap.
5348 Roo.namespace('Roo.form');
5349  /**
5350  * @class Roo.form.Action
5351  * Internal Class used to handle form actions
5352  * @constructor
5353  * @param {Roo.form.BasicForm} el The form element or its id
5354  * @param {Object} config Configuration options
5355  */
5356
5357  
5358  
5359 // define the action interface
5360 Roo.form.Action = function(form, options){
5361     this.form = form;
5362     this.options = options || {};
5363 };
5364 /**
5365  * Client Validation Failed
5366  * @const 
5367  */
5368 Roo.form.Action.CLIENT_INVALID = 'client';
5369 /**
5370  * Server Validation Failed
5371  * @const 
5372  */
5373 Roo.form.Action.SERVER_INVALID = 'server';
5374  /**
5375  * Connect to Server Failed
5376  * @const 
5377  */
5378 Roo.form.Action.CONNECT_FAILURE = 'connect';
5379 /**
5380  * Reading Data from Server Failed
5381  * @const 
5382  */
5383 Roo.form.Action.LOAD_FAILURE = 'load';
5384
5385 Roo.form.Action.prototype = {
5386     type : 'default',
5387     failureType : undefined,
5388     response : undefined,
5389     result : undefined,
5390
5391     // interface method
5392     run : function(options){
5393
5394     },
5395
5396     // interface method
5397     success : function(response){
5398
5399     },
5400
5401     // interface method
5402     handleResponse : function(response){
5403
5404     },
5405
5406     // default connection failure
5407     failure : function(response){
5408         
5409         this.response = response;
5410         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5411         this.form.afterAction(this, false);
5412     },
5413
5414     processResponse : function(response){
5415         this.response = response;
5416         if(!response.responseText){
5417             return true;
5418         }
5419         this.result = this.handleResponse(response);
5420         return this.result;
5421     },
5422
5423     // utility functions used internally
5424     getUrl : function(appendParams){
5425         var url = this.options.url || this.form.url || this.form.el.dom.action;
5426         if(appendParams){
5427             var p = this.getParams();
5428             if(p){
5429                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5430             }
5431         }
5432         return url;
5433     },
5434
5435     getMethod : function(){
5436         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5437     },
5438
5439     getParams : function(){
5440         var bp = this.form.baseParams;
5441         var p = this.options.params;
5442         if(p){
5443             if(typeof p == "object"){
5444                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5445             }else if(typeof p == 'string' && bp){
5446                 p += '&' + Roo.urlEncode(bp);
5447             }
5448         }else if(bp){
5449             p = Roo.urlEncode(bp);
5450         }
5451         return p;
5452     },
5453
5454     createCallback : function(){
5455         return {
5456             success: this.success,
5457             failure: this.failure,
5458             scope: this,
5459             timeout: (this.form.timeout*1000),
5460             upload: this.form.fileUpload ? this.success : undefined
5461         };
5462     }
5463 };
5464
5465 Roo.form.Action.Submit = function(form, options){
5466     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5467 };
5468
5469 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5470     type : 'submit',
5471
5472     haveProgress : false,
5473     uploadComplete : false,
5474     
5475     // uploadProgress indicator.
5476     uploadProgress : function()
5477     {
5478         if (!this.form.progressUrl) {
5479             return;
5480         }
5481         
5482         if (!this.haveProgress) {
5483             Roo.MessageBox.progress("Uploading", "Uploading");
5484         }
5485         if (this.uploadComplete) {
5486            Roo.MessageBox.hide();
5487            return;
5488         }
5489         
5490         this.haveProgress = true;
5491    
5492         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5493         
5494         var c = new Roo.data.Connection();
5495         c.request({
5496             url : this.form.progressUrl,
5497             params: {
5498                 id : uid
5499             },
5500             method: 'GET',
5501             success : function(req){
5502                //console.log(data);
5503                 var rdata = false;
5504                 var edata;
5505                 try  {
5506                    rdata = Roo.decode(req.responseText)
5507                 } catch (e) {
5508                     Roo.log("Invalid data from server..");
5509                     Roo.log(edata);
5510                     return;
5511                 }
5512                 if (!rdata || !rdata.success) {
5513                     Roo.log(rdata);
5514                     Roo.MessageBox.alert(Roo.encode(rdata));
5515                     return;
5516                 }
5517                 var data = rdata.data;
5518                 
5519                 if (this.uploadComplete) {
5520                    Roo.MessageBox.hide();
5521                    return;
5522                 }
5523                    
5524                 if (data){
5525                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5526                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5527                     );
5528                 }
5529                 this.uploadProgress.defer(2000,this);
5530             },
5531        
5532             failure: function(data) {
5533                 Roo.log('progress url failed ');
5534                 Roo.log(data);
5535             },
5536             scope : this
5537         });
5538            
5539     },
5540     
5541     
5542     run : function()
5543     {
5544         // run get Values on the form, so it syncs any secondary forms.
5545         this.form.getValues();
5546         
5547         var o = this.options;
5548         var method = this.getMethod();
5549         var isPost = method == 'POST';
5550         if(o.clientValidation === false || this.form.isValid()){
5551             
5552             if (this.form.progressUrl) {
5553                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5554                     (new Date() * 1) + '' + Math.random());
5555                     
5556             } 
5557             
5558             
5559             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5560                 form:this.form.el.dom,
5561                 url:this.getUrl(!isPost),
5562                 method: method,
5563                 params:isPost ? this.getParams() : null,
5564                 isUpload: this.form.fileUpload
5565             }));
5566             
5567             this.uploadProgress();
5568
5569         }else if (o.clientValidation !== false){ // client validation failed
5570             this.failureType = Roo.form.Action.CLIENT_INVALID;
5571             this.form.afterAction(this, false);
5572         }
5573     },
5574
5575     success : function(response)
5576     {
5577         this.uploadComplete= true;
5578         if (this.haveProgress) {
5579             Roo.MessageBox.hide();
5580         }
5581         
5582         
5583         var result = this.processResponse(response);
5584         if(result === true || result.success){
5585             this.form.afterAction(this, true);
5586             return;
5587         }
5588         if(result.errors){
5589             this.form.markInvalid(result.errors);
5590             this.failureType = Roo.form.Action.SERVER_INVALID;
5591         }
5592         this.form.afterAction(this, false);
5593     },
5594     failure : function(response)
5595     {
5596         this.uploadComplete= true;
5597         if (this.haveProgress) {
5598             Roo.MessageBox.hide();
5599         }
5600         
5601         this.response = response;
5602         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5603         this.form.afterAction(this, false);
5604     },
5605     
5606     handleResponse : function(response){
5607         if(this.form.errorReader){
5608             var rs = this.form.errorReader.read(response);
5609             var errors = [];
5610             if(rs.records){
5611                 for(var i = 0, len = rs.records.length; i < len; i++) {
5612                     var r = rs.records[i];
5613                     errors[i] = r.data;
5614                 }
5615             }
5616             if(errors.length < 1){
5617                 errors = null;
5618             }
5619             return {
5620                 success : rs.success,
5621                 errors : errors
5622             };
5623         }
5624         var ret = false;
5625         try {
5626             ret = Roo.decode(response.responseText);
5627         } catch (e) {
5628             ret = {
5629                 success: false,
5630                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5631                 errors : []
5632             };
5633         }
5634         return ret;
5635         
5636     }
5637 });
5638
5639
5640 Roo.form.Action.Load = function(form, options){
5641     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5642     this.reader = this.form.reader;
5643 };
5644
5645 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5646     type : 'load',
5647
5648     run : function(){
5649         
5650         Roo.Ajax.request(Roo.apply(
5651                 this.createCallback(), {
5652                     method:this.getMethod(),
5653                     url:this.getUrl(false),
5654                     params:this.getParams()
5655         }));
5656     },
5657
5658     success : function(response){
5659         
5660         var result = this.processResponse(response);
5661         if(result === true || !result.success || !result.data){
5662             this.failureType = Roo.form.Action.LOAD_FAILURE;
5663             this.form.afterAction(this, false);
5664             return;
5665         }
5666         this.form.clearInvalid();
5667         this.form.setValues(result.data);
5668         this.form.afterAction(this, true);
5669     },
5670
5671     handleResponse : function(response){
5672         if(this.form.reader){
5673             var rs = this.form.reader.read(response);
5674             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5675             return {
5676                 success : rs.success,
5677                 data : data
5678             };
5679         }
5680         return Roo.decode(response.responseText);
5681     }
5682 });
5683
5684 Roo.form.Action.ACTION_TYPES = {
5685     'load' : Roo.form.Action.Load,
5686     'submit' : Roo.form.Action.Submit
5687 };/*
5688  * - LGPL
5689  *
5690  * form
5691  * 
5692  */
5693
5694 /**
5695  * @class Roo.bootstrap.Form
5696  * @extends Roo.bootstrap.Component
5697  * Bootstrap Form class
5698  * @cfg {String} method  GET | POST (default POST)
5699  * @cfg {String} labelAlign top | left (default top)
5700   * @cfg {String} align left  | right - for navbars
5701
5702  * 
5703  * @constructor
5704  * Create a new Form
5705  * @param {Object} config The config object
5706  */
5707
5708
5709 Roo.bootstrap.Form = function(config){
5710     Roo.bootstrap.Form.superclass.constructor.call(this, config);
5711     this.addEvents({
5712         /**
5713          * @event clientvalidation
5714          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5715          * @param {Form} this
5716          * @param {Boolean} valid true if the form has passed client-side validation
5717          */
5718         clientvalidation: true,
5719         /**
5720          * @event beforeaction
5721          * Fires before any action is performed. Return false to cancel the action.
5722          * @param {Form} this
5723          * @param {Action} action The action to be performed
5724          */
5725         beforeaction: true,
5726         /**
5727          * @event actionfailed
5728          * Fires when an action fails.
5729          * @param {Form} this
5730          * @param {Action} action The action that failed
5731          */
5732         actionfailed : true,
5733         /**
5734          * @event actioncomplete
5735          * Fires when an action is completed.
5736          * @param {Form} this
5737          * @param {Action} action The action that completed
5738          */
5739         actioncomplete : true
5740     });
5741     
5742 };
5743
5744 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
5745       
5746      /**
5747      * @cfg {String} method
5748      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
5749      */
5750     method : 'POST',
5751     /**
5752      * @cfg {String} url
5753      * The URL to use for form actions if one isn't supplied in the action options.
5754      */
5755     /**
5756      * @cfg {Boolean} fileUpload
5757      * Set to true if this form is a file upload.
5758      */
5759      
5760     /**
5761      * @cfg {Object} baseParams
5762      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
5763      */
5764       
5765     /**
5766      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5767      */
5768     timeout: 30,
5769     /**
5770      * @cfg {Sting} align (left|right) for navbar forms
5771      */
5772     align : 'left',
5773
5774     // private
5775     activeAction : null,
5776  
5777     /**
5778      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5779      * element by passing it or its id or mask the form itself by passing in true.
5780      * @type Mixed
5781      */
5782     waitMsgTarget : false,
5783     
5784      
5785     
5786     /**
5787      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5788      * element by passing it or its id or mask the form itself by passing in true.
5789      * @type Mixed
5790      */
5791     
5792     getAutoCreate : function(){
5793         
5794         var cfg = {
5795             tag: 'form',
5796             method : this.method || 'POST',
5797             id : this.id || Roo.id(),
5798             cls : ''
5799         }
5800         if (this.parent().xtype.match(/^Nav/)) {
5801             cfg.cls = 'navbar-form navbar-' + this.align;
5802             
5803         }
5804         
5805         if (this.labelAlign == 'left' ) {
5806             cfg.cls += ' form-horizontal';
5807         }
5808         
5809         
5810         return cfg;
5811     },
5812     initEvents : function()
5813     {
5814         this.el.on('submit', this.onSubmit, this);
5815         this.el.on('keypress', function(e) {
5816             if (e.getCharCode() != 13) {
5817                 return true;
5818             }
5819             e.preventDefault();
5820             return false;
5821         });
5822         
5823     },
5824     // private
5825     onSubmit : function(e){
5826         e.stopEvent();
5827     },
5828     
5829      /**
5830      * Returns true if client-side validation on the form is successful.
5831      * @return Boolean
5832      */
5833     isValid : function(){
5834         var items = this.getItems();
5835         var valid = true;
5836         items.each(function(f){
5837            if(!f.validate()){
5838                valid = false;
5839                
5840            }
5841         });
5842         return valid;
5843     },
5844     /**
5845      * Returns true if any fields in this form have changed since their original load.
5846      * @return Boolean
5847      */
5848     isDirty : function(){
5849         var dirty = false;
5850         var items = this.getItems();
5851         items.each(function(f){
5852            if(f.isDirty()){
5853                dirty = true;
5854                return false;
5855            }
5856            return true;
5857         });
5858         return dirty;
5859     },
5860      /**
5861      * Performs a predefined action (submit or load) or custom actions you define on this form.
5862      * @param {String} actionName The name of the action type
5863      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
5864      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
5865      * accept other config options):
5866      * <pre>
5867 Property          Type             Description
5868 ----------------  ---------------  ----------------------------------------------------------------------------------
5869 url               String           The url for the action (defaults to the form's url)
5870 method            String           The form method to use (defaults to the form's method, or POST if not defined)
5871 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
5872 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
5873                                    validate the form on the client (defaults to false)
5874      * </pre>
5875      * @return {BasicForm} this
5876      */
5877     doAction : function(action, options){
5878         if(typeof action == 'string'){
5879             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
5880         }
5881         if(this.fireEvent('beforeaction', this, action) !== false){
5882             this.beforeAction(action);
5883             action.run.defer(100, action);
5884         }
5885         return this;
5886     },
5887     
5888     // private
5889     beforeAction : function(action){
5890         var o = action.options;
5891         
5892         // not really supported yet.. ??
5893         
5894         //if(this.waitMsgTarget === true){
5895             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
5896         //}else if(this.waitMsgTarget){
5897         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
5898         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
5899         //}else {
5900         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
5901        // }
5902          
5903     },
5904
5905     // private
5906     afterAction : function(action, success){
5907         this.activeAction = null;
5908         var o = action.options;
5909         
5910         //if(this.waitMsgTarget === true){
5911             this.el.unmask();
5912         //}else if(this.waitMsgTarget){
5913         //    this.waitMsgTarget.unmask();
5914         //}else{
5915         //    Roo.MessageBox.updateProgress(1);
5916         //    Roo.MessageBox.hide();
5917        // }
5918         // 
5919         if(success){
5920             if(o.reset){
5921                 this.reset();
5922             }
5923             Roo.callback(o.success, o.scope, [this, action]);
5924             this.fireEvent('actioncomplete', this, action);
5925             
5926         }else{
5927             
5928             // failure condition..
5929             // we have a scenario where updates need confirming.
5930             // eg. if a locking scenario exists..
5931             // we look for { errors : { needs_confirm : true }} in the response.
5932             if (
5933                 (typeof(action.result) != 'undefined')  &&
5934                 (typeof(action.result.errors) != 'undefined')  &&
5935                 (typeof(action.result.errors.needs_confirm) != 'undefined')
5936            ){
5937                 var _t = this;
5938                 Roo.log("not supported yet");
5939                  /*
5940                 
5941                 Roo.MessageBox.confirm(
5942                     "Change requires confirmation",
5943                     action.result.errorMsg,
5944                     function(r) {
5945                         if (r != 'yes') {
5946                             return;
5947                         }
5948                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
5949                     }
5950                     
5951                 );
5952                 */
5953                 
5954                 
5955                 return;
5956             }
5957             
5958             Roo.callback(o.failure, o.scope, [this, action]);
5959             // show an error message if no failed handler is set..
5960             if (!this.hasListener('actionfailed')) {
5961                 Roo.log("need to add dialog support");
5962                 /*
5963                 Roo.MessageBox.alert("Error",
5964                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
5965                         action.result.errorMsg :
5966                         "Saving Failed, please check your entries or try again"
5967                 );
5968                 */
5969             }
5970             
5971             this.fireEvent('actionfailed', this, action);
5972         }
5973         
5974     },
5975     /**
5976      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
5977      * @param {String} id The value to search for
5978      * @return Field
5979      */
5980     findField : function(id){
5981         var items = this.getItems();
5982         var field = items.get(id);
5983         if(!field){
5984              items.each(function(f){
5985                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
5986                     field = f;
5987                     return false;
5988                 }
5989                 return true;
5990             });
5991         }
5992         return field || null;
5993     },
5994      /**
5995      * Mark fields in this form invalid in bulk.
5996      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
5997      * @return {BasicForm} this
5998      */
5999     markInvalid : function(errors){
6000         if(errors instanceof Array){
6001             for(var i = 0, len = errors.length; i < len; i++){
6002                 var fieldError = errors[i];
6003                 var f = this.findField(fieldError.id);
6004                 if(f){
6005                     f.markInvalid(fieldError.msg);
6006                 }
6007             }
6008         }else{
6009             var field, id;
6010             for(id in errors){
6011                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6012                     field.markInvalid(errors[id]);
6013                 }
6014             }
6015         }
6016         //Roo.each(this.childForms || [], function (f) {
6017         //    f.markInvalid(errors);
6018         //});
6019         
6020         return this;
6021     },
6022
6023     /**
6024      * Set values for fields in this form in bulk.
6025      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6026      * @return {BasicForm} this
6027      */
6028     setValues : function(values){
6029         if(values instanceof Array){ // array of objects
6030             for(var i = 0, len = values.length; i < len; i++){
6031                 var v = values[i];
6032                 var f = this.findField(v.id);
6033                 if(f){
6034                     f.setValue(v.value);
6035                     if(this.trackResetOnLoad){
6036                         f.originalValue = f.getValue();
6037                     }
6038                 }
6039             }
6040         }else{ // object hash
6041             var field, id;
6042             for(id in values){
6043                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6044                     
6045                     if (field.setFromData && 
6046                         field.valueField && 
6047                         field.displayField &&
6048                         // combos' with local stores can 
6049                         // be queried via setValue()
6050                         // to set their value..
6051                         (field.store && !field.store.isLocal)
6052                         ) {
6053                         // it's a combo
6054                         var sd = { };
6055                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6056                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6057                         field.setFromData(sd);
6058                         
6059                     } else {
6060                         field.setValue(values[id]);
6061                     }
6062                     
6063                     
6064                     if(this.trackResetOnLoad){
6065                         field.originalValue = field.getValue();
6066                     }
6067                 }
6068             }
6069         }
6070          
6071         //Roo.each(this.childForms || [], function (f) {
6072         //    f.setValues(values);
6073         //});
6074                 
6075         return this;
6076     },
6077
6078     /**
6079      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6080      * they are returned as an array.
6081      * @param {Boolean} asString
6082      * @return {Object}
6083      */
6084     getValues : function(asString){
6085         //if (this.childForms) {
6086             // copy values from the child forms
6087         //    Roo.each(this.childForms, function (f) {
6088         //        this.setValues(f.getValues());
6089         //    }, this);
6090         //}
6091         
6092         
6093         
6094         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6095         if(asString === true){
6096             return fs;
6097         }
6098         return Roo.urlDecode(fs);
6099     },
6100     
6101     /**
6102      * Returns the fields in this form as an object with key/value pairs. 
6103      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6104      * @return {Object}
6105      */
6106     getFieldValues : function(with_hidden)
6107     {
6108         var items = this.getItems();
6109         var ret = {};
6110         items.each(function(f){
6111             if (!f.getName()) {
6112                 return;
6113             }
6114             var v = f.getValue();
6115             if (f.inputType =='radio') {
6116                 if (typeof(ret[f.getName()]) == 'undefined') {
6117                     ret[f.getName()] = ''; // empty..
6118                 }
6119                 
6120                 if (!f.el.dom.checked) {
6121                     return;
6122                     
6123                 }
6124                 v = f.el.dom.value;
6125                 
6126             }
6127             
6128             // not sure if this supported any more..
6129             if ((typeof(v) == 'object') && f.getRawValue) {
6130                 v = f.getRawValue() ; // dates..
6131             }
6132             // combo boxes where name != hiddenName...
6133             if (f.name != f.getName()) {
6134                 ret[f.name] = f.getRawValue();
6135             }
6136             ret[f.getName()] = v;
6137         });
6138         
6139         return ret;
6140     },
6141
6142     /**
6143      * Clears all invalid messages in this form.
6144      * @return {BasicForm} this
6145      */
6146     clearInvalid : function(){
6147         var items = this.getItems();
6148         
6149         items.each(function(f){
6150            f.clearInvalid();
6151         });
6152         
6153         
6154         
6155         return this;
6156     },
6157
6158     /**
6159      * Resets this form.
6160      * @return {BasicForm} this
6161      */
6162     reset : function(){
6163         var items = this.getItems();
6164         items.each(function(f){
6165             f.reset();
6166         });
6167         
6168         Roo.each(this.childForms || [], function (f) {
6169             f.reset();
6170         });
6171        
6172         
6173         return this;
6174     },
6175     getItems : function()
6176     {
6177         var r=new Roo.util.MixedCollection(false, function(o){
6178             return o.id || (o.id = Roo.id());
6179         });
6180         var iter = function(el) {
6181             if (el.inputEl) {
6182                 r.add(el);
6183             }
6184             if (!el.items) {
6185                 return;
6186             }
6187             Roo.each(el.items,function(e) {
6188                 iter(e);
6189             });
6190             
6191             
6192         };
6193         iter(this);
6194         return r;
6195         
6196         
6197         
6198         
6199     }
6200     
6201 });
6202
6203  
6204 /*
6205  * Based on:
6206  * Ext JS Library 1.1.1
6207  * Copyright(c) 2006-2007, Ext JS, LLC.
6208  *
6209  * Originally Released Under LGPL - original licence link has changed is not relivant.
6210  *
6211  * Fork - LGPL
6212  * <script type="text/javascript">
6213  */
6214 /**
6215  * @class Roo.form.VTypes
6216  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6217  * @singleton
6218  */
6219 Roo.form.VTypes = function(){
6220     // closure these in so they are only created once.
6221     var alpha = /^[a-zA-Z_]+$/;
6222     var alphanum = /^[a-zA-Z0-9_]+$/;
6223     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6224     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6225
6226     // All these messages and functions are configurable
6227     return {
6228         /**
6229          * The function used to validate email addresses
6230          * @param {String} value The email address
6231          */
6232         'email' : function(v){
6233             return email.test(v);
6234         },
6235         /**
6236          * The error text to display when the email validation function returns false
6237          * @type String
6238          */
6239         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6240         /**
6241          * The keystroke filter mask to be applied on email input
6242          * @type RegExp
6243          */
6244         'emailMask' : /[a-z0-9_\.\-@]/i,
6245
6246         /**
6247          * The function used to validate URLs
6248          * @param {String} value The URL
6249          */
6250         'url' : function(v){
6251             return url.test(v);
6252         },
6253         /**
6254          * The error text to display when the url validation function returns false
6255          * @type String
6256          */
6257         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6258         
6259         /**
6260          * The function used to validate alpha values
6261          * @param {String} value The value
6262          */
6263         'alpha' : function(v){
6264             return alpha.test(v);
6265         },
6266         /**
6267          * The error text to display when the alpha validation function returns false
6268          * @type String
6269          */
6270         'alphaText' : 'This field should only contain letters and _',
6271         /**
6272          * The keystroke filter mask to be applied on alpha input
6273          * @type RegExp
6274          */
6275         'alphaMask' : /[a-z_]/i,
6276
6277         /**
6278          * The function used to validate alphanumeric values
6279          * @param {String} value The value
6280          */
6281         'alphanum' : function(v){
6282             return alphanum.test(v);
6283         },
6284         /**
6285          * The error text to display when the alphanumeric validation function returns false
6286          * @type String
6287          */
6288         'alphanumText' : 'This field should only contain letters, numbers and _',
6289         /**
6290          * The keystroke filter mask to be applied on alphanumeric input
6291          * @type RegExp
6292          */
6293         'alphanumMask' : /[a-z0-9_]/i
6294     };
6295 }();/*
6296  * - LGPL
6297  *
6298  * Input
6299  * 
6300  */
6301
6302 /**
6303  * @class Roo.bootstrap.Input
6304  * @extends Roo.bootstrap.Component
6305  * Bootstrap Input class
6306  * @cfg {Boolean} disabled is it disabled
6307  * @cfg {String} fieldLabel - the label associated
6308  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6309  * @cfg {String} name name of the input
6310  * @cfg {string} fieldLabel - the label associated
6311  * @cfg {string}  inputType - input / file submit ...
6312  * @cfg {string} placeholder - placeholder to put in text.
6313  * @cfg {string}  before - input group add on before
6314  * @cfg {string} after - input group add on after
6315  * @cfg {string} size - (lg|sm) or leave empty..
6316  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6317  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6318  * @cfg {Number} md colspan out of 12 for computer-sized screens
6319  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6320  * @cfg {string} value default value of the input
6321  * @cfg {Number} labelWidth set the width of label (0-12)
6322  * @cfg {String} labelAlign (top|left)
6323  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6324  * @cfg {String} align (left|center|right) Default left
6325  * 
6326  * 
6327  * @constructor
6328  * Create a new Input
6329  * @param {Object} config The config object
6330  */
6331
6332 Roo.bootstrap.Input = function(config){
6333     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6334    
6335         this.addEvents({
6336             /**
6337              * @event focus
6338              * Fires when this field receives input focus.
6339              * @param {Roo.form.Field} this
6340              */
6341             focus : true,
6342             /**
6343              * @event blur
6344              * Fires when this field loses input focus.
6345              * @param {Roo.form.Field} this
6346              */
6347             blur : true,
6348             /**
6349              * @event specialkey
6350              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6351              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6352              * @param {Roo.form.Field} this
6353              * @param {Roo.EventObject} e The event object
6354              */
6355             specialkey : true,
6356             /**
6357              * @event change
6358              * Fires just before the field blurs if the field value has changed.
6359              * @param {Roo.form.Field} this
6360              * @param {Mixed} newValue The new value
6361              * @param {Mixed} oldValue The original value
6362              */
6363             change : true,
6364             /**
6365              * @event invalid
6366              * Fires after the field has been marked as invalid.
6367              * @param {Roo.form.Field} this
6368              * @param {String} msg The validation message
6369              */
6370             invalid : true,
6371             /**
6372              * @event valid
6373              * Fires after the field has been validated with no errors.
6374              * @param {Roo.form.Field} this
6375              */
6376             valid : true,
6377              /**
6378              * @event keyup
6379              * Fires after the key up
6380              * @param {Roo.form.Field} this
6381              * @param {Roo.EventObject}  e The event Object
6382              */
6383             keyup : true
6384         });
6385 };
6386
6387 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6388      /**
6389      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6390       automatic validation (defaults to "keyup").
6391      */
6392     validationEvent : "keyup",
6393      /**
6394      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6395      */
6396     validateOnBlur : true,
6397     /**
6398      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6399      */
6400     validationDelay : 250,
6401      /**
6402      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6403      */
6404     focusClass : "x-form-focus",  // not needed???
6405     
6406        
6407     /**
6408      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6409      */
6410     invalidClass : "has-error",
6411     
6412     /**
6413      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6414      */
6415     selectOnFocus : false,
6416     
6417      /**
6418      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6419      */
6420     maskRe : null,
6421        /**
6422      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6423      */
6424     vtype : null,
6425     
6426       /**
6427      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6428      */
6429     disableKeyFilter : false,
6430     
6431        /**
6432      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6433      */
6434     disabled : false,
6435      /**
6436      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6437      */
6438     allowBlank : true,
6439     /**
6440      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6441      */
6442     blankText : "This field is required",
6443     
6444      /**
6445      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6446      */
6447     minLength : 0,
6448     /**
6449      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6450      */
6451     maxLength : Number.MAX_VALUE,
6452     /**
6453      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6454      */
6455     minLengthText : "The minimum length for this field is {0}",
6456     /**
6457      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6458      */
6459     maxLengthText : "The maximum length for this field is {0}",
6460   
6461     
6462     /**
6463      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6464      * If available, this function will be called only after the basic validators all return true, and will be passed the
6465      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6466      */
6467     validator : null,
6468     /**
6469      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6470      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6471      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6472      */
6473     regex : null,
6474     /**
6475      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6476      */
6477     regexText : "",
6478     
6479     
6480     
6481     fieldLabel : '',
6482     inputType : 'text',
6483     
6484     name : false,
6485     placeholder: false,
6486     before : false,
6487     after : false,
6488     size : false,
6489     // private
6490     hasFocus : false,
6491     preventMark: false,
6492     isFormField : true,
6493     value : '',
6494     labelWidth : 2,
6495     labelAlign : false,
6496     readOnly : false,
6497     align : false,
6498     
6499     parentLabelAlign : function()
6500     {
6501         var parent = this;
6502         while (parent.parent()) {
6503             parent = parent.parent();
6504             if (typeof(parent.labelAlign) !='undefined') {
6505                 return parent.labelAlign;
6506             }
6507         }
6508         return 'left';
6509         
6510     },
6511     
6512     getAutoCreate : function(){
6513         
6514         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6515         
6516         var id = Roo.id();
6517         
6518         var cfg = {};
6519         
6520         if(this.inputType != 'hidden'){
6521             cfg.cls = 'form-group' //input-group
6522         }
6523         
6524         var input =  {
6525             tag: 'input',
6526             id : id,
6527             type : this.inputType,
6528             value : this.value,
6529             cls : 'form-control',
6530             placeholder : this.placeholder || ''
6531             
6532         };
6533         
6534         if(this.align){
6535             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6536         }
6537         
6538         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6539             input.maxLength = this.maxLength;
6540         }
6541         
6542         if (this.disabled) {
6543             input.disabled=true;
6544         }
6545         
6546         if (this.readOnly) {
6547             input.readonly=true;
6548         }
6549         
6550         if (this.name) {
6551             input.name = this.name;
6552         }
6553         if (this.size) {
6554             input.cls += ' input-' + this.size;
6555         }
6556         var settings=this;
6557         ['xs','sm','md','lg'].map(function(size){
6558             if (settings[size]) {
6559                 cfg.cls += ' col-' + size + '-' + settings[size];
6560             }
6561         });
6562         
6563         var inputblock = input;
6564         
6565         if (this.before || this.after) {
6566             
6567             inputblock = {
6568                 cls : 'input-group',
6569                 cn :  [] 
6570             };
6571             if (this.before && typeof(this.before) == 'string') {
6572                 
6573                 inputblock.cn.push({
6574                     tag :'span',
6575                     cls : 'roo-input-before input-group-addon',
6576                     html : this.before
6577                 });
6578             }
6579             if (this.before && typeof(this.before) == 'object') {
6580                 this.before = Roo.factory(this.before);
6581                 Roo.log(this.before);
6582                 inputblock.cn.push({
6583                     tag :'span',
6584                     cls : 'roo-input-before input-group-' +
6585                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6586                 });
6587             }
6588             
6589             inputblock.cn.push(input);
6590             
6591             if (this.after && typeof(this.after) == 'string') {
6592                 inputblock.cn.push({
6593                     tag :'span',
6594                     cls : 'roo-input-after input-group-addon',
6595                     html : this.after
6596                 });
6597             }
6598             if (this.after && typeof(this.after) == 'object') {
6599                 this.after = Roo.factory(this.after);
6600                 Roo.log(this.after);
6601                 inputblock.cn.push({
6602                     tag :'span',
6603                     cls : 'roo-input-after input-group-' +
6604                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6605                 });
6606             }
6607         };
6608         
6609         if (align ==='left' && this.fieldLabel.length) {
6610                 Roo.log("left and has label");
6611                 cfg.cn = [
6612                     
6613                     {
6614                         tag: 'label',
6615                         'for' :  id,
6616                         cls : 'control-label col-sm-' + this.labelWidth,
6617                         html : this.fieldLabel
6618                         
6619                     },
6620                     {
6621                         cls : "col-sm-" + (12 - this.labelWidth), 
6622                         cn: [
6623                             inputblock
6624                         ]
6625                     }
6626                     
6627                 ];
6628         } else if ( this.fieldLabel.length) {
6629                 Roo.log(" label");
6630                  cfg.cn = [
6631                    
6632                     {
6633                         tag: 'label',
6634                         //cls : 'input-group-addon',
6635                         html : this.fieldLabel
6636                         
6637                     },
6638                     
6639                     inputblock
6640                     
6641                 ];
6642
6643         } else {
6644             
6645                 Roo.log(" no label && no align");
6646                 cfg.cn = [
6647                     
6648                         inputblock
6649                     
6650                 ];
6651                 
6652                 
6653         };
6654         Roo.log('input-parentType: ' + this.parentType);
6655         
6656         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6657            cfg.cls += ' navbar-form';
6658            Roo.log(cfg);
6659         }
6660         
6661         return cfg;
6662         
6663     },
6664     /**
6665      * return the real input element.
6666      */
6667     inputEl: function ()
6668     {
6669         return this.el.select('input.form-control',true).first();
6670     },
6671     setDisabled : function(v)
6672     {
6673         var i  = this.inputEl().dom;
6674         if (!v) {
6675             i.removeAttribute('disabled');
6676             return;
6677             
6678         }
6679         i.setAttribute('disabled','true');
6680     },
6681     initEvents : function()
6682     {
6683         
6684         this.inputEl().on("keydown" , this.fireKey,  this);
6685         this.inputEl().on("focus", this.onFocus,  this);
6686         this.inputEl().on("blur", this.onBlur,  this);
6687         
6688         this.inputEl().relayEvent('keyup', this);
6689
6690         // reference to original value for reset
6691         this.originalValue = this.getValue();
6692         //Roo.form.TextField.superclass.initEvents.call(this);
6693         if(this.validationEvent == 'keyup'){
6694             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
6695             this.inputEl().on('keyup', this.filterValidation, this);
6696         }
6697         else if(this.validationEvent !== false){
6698             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
6699         }
6700         
6701         if(this.selectOnFocus){
6702             this.on("focus", this.preFocus, this);
6703             
6704         }
6705         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
6706             this.inputEl().on("keypress", this.filterKeys, this);
6707         }
6708        /* if(this.grow){
6709             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
6710             this.el.on("click", this.autoSize,  this);
6711         }
6712         */
6713         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
6714             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
6715         }
6716         
6717         if (typeof(this.before) == 'object') {
6718             this.before.render(this.el.select('.roo-input-before',true).first());
6719         }
6720         if (typeof(this.after) == 'object') {
6721             this.after.render(this.el.select('.roo-input-after',true).first());
6722         }
6723         
6724         
6725     },
6726     filterValidation : function(e){
6727         if(!e.isNavKeyPress()){
6728             this.validationTask.delay(this.validationDelay);
6729         }
6730     },
6731      /**
6732      * Validates the field value
6733      * @return {Boolean} True if the value is valid, else false
6734      */
6735     validate : function(){
6736         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
6737         if(this.disabled || this.validateValue(this.getRawValue())){
6738             this.clearInvalid();
6739             return true;
6740         }
6741         return false;
6742     },
6743     
6744     
6745     /**
6746      * Validates a value according to the field's validation rules and marks the field as invalid
6747      * if the validation fails
6748      * @param {Mixed} value The value to validate
6749      * @return {Boolean} True if the value is valid, else false
6750      */
6751     validateValue : function(value){
6752         if(value.length < 1)  { // if it's blank
6753              if(this.allowBlank){
6754                 this.clearInvalid();
6755                 return true;
6756              }else{
6757                 this.markInvalid(this.blankText);
6758                 return false;
6759              }
6760         }
6761         if(value.length < this.minLength){
6762             this.markInvalid(String.format(this.minLengthText, this.minLength));
6763             return false;
6764         }
6765         if(value.length > this.maxLength){
6766             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
6767             return false;
6768         }
6769         if(this.vtype){
6770             var vt = Roo.form.VTypes;
6771             if(!vt[this.vtype](value, this)){
6772                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
6773                 return false;
6774             }
6775         }
6776         if(typeof this.validator == "function"){
6777             var msg = this.validator(value);
6778             if(msg !== true){
6779                 this.markInvalid(msg);
6780                 return false;
6781             }
6782         }
6783         if(this.regex && !this.regex.test(value)){
6784             this.markInvalid(this.regexText);
6785             return false;
6786         }
6787         return true;
6788     },
6789
6790     
6791     
6792      // private
6793     fireKey : function(e){
6794         //Roo.log('field ' + e.getKey());
6795         if(e.isNavKeyPress()){
6796             this.fireEvent("specialkey", this, e);
6797         }
6798     },
6799     focus : function (selectText){
6800         if(this.rendered){
6801             this.inputEl().focus();
6802             if(selectText === true){
6803                 this.inputEl().dom.select();
6804             }
6805         }
6806         return this;
6807     } ,
6808     
6809     onFocus : function(){
6810         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6811            // this.el.addClass(this.focusClass);
6812         }
6813         if(!this.hasFocus){
6814             this.hasFocus = true;
6815             this.startValue = this.getValue();
6816             this.fireEvent("focus", this);
6817         }
6818     },
6819     
6820     beforeBlur : Roo.emptyFn,
6821
6822     
6823     // private
6824     onBlur : function(){
6825         this.beforeBlur();
6826         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6827             //this.el.removeClass(this.focusClass);
6828         }
6829         this.hasFocus = false;
6830         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
6831             this.validate();
6832         }
6833         var v = this.getValue();
6834         if(String(v) !== String(this.startValue)){
6835             this.fireEvent('change', this, v, this.startValue);
6836         }
6837         this.fireEvent("blur", this);
6838     },
6839     
6840     /**
6841      * Resets the current field value to the originally loaded value and clears any validation messages
6842      */
6843     reset : function(){
6844         this.setValue(this.originalValue);
6845         this.clearInvalid();
6846     },
6847      /**
6848      * Returns the name of the field
6849      * @return {Mixed} name The name field
6850      */
6851     getName: function(){
6852         return this.name;
6853     },
6854      /**
6855      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
6856      * @return {Mixed} value The field value
6857      */
6858     getValue : function(){
6859         return this.inputEl().getValue();
6860     },
6861     /**
6862      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
6863      * @return {Mixed} value The field value
6864      */
6865     getRawValue : function(){
6866         var v = this.inputEl().getValue();
6867         
6868         return v;
6869     },
6870     
6871     /**
6872      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
6873      * @param {Mixed} value The value to set
6874      */
6875     setRawValue : function(v){
6876         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6877     },
6878     
6879     selectText : function(start, end){
6880         var v = this.getRawValue();
6881         if(v.length > 0){
6882             start = start === undefined ? 0 : start;
6883             end = end === undefined ? v.length : end;
6884             var d = this.inputEl().dom;
6885             if(d.setSelectionRange){
6886                 d.setSelectionRange(start, end);
6887             }else if(d.createTextRange){
6888                 var range = d.createTextRange();
6889                 range.moveStart("character", start);
6890                 range.moveEnd("character", v.length-end);
6891                 range.select();
6892             }
6893         }
6894     },
6895     
6896     /**
6897      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
6898      * @param {Mixed} value The value to set
6899      */
6900     setValue : function(v){
6901         this.value = v;
6902         if(this.rendered){
6903             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6904             this.validate();
6905         }
6906     },
6907     
6908     /*
6909     processValue : function(value){
6910         if(this.stripCharsRe){
6911             var newValue = value.replace(this.stripCharsRe, '');
6912             if(newValue !== value){
6913                 this.setRawValue(newValue);
6914                 return newValue;
6915             }
6916         }
6917         return value;
6918     },
6919   */
6920     preFocus : function(){
6921         
6922         if(this.selectOnFocus){
6923             this.inputEl().dom.select();
6924         }
6925     },
6926     filterKeys : function(e){
6927         var k = e.getKey();
6928         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
6929             return;
6930         }
6931         var c = e.getCharCode(), cc = String.fromCharCode(c);
6932         if(Roo.isIE && (e.isSpecialKey() || !cc)){
6933             return;
6934         }
6935         if(!this.maskRe.test(cc)){
6936             e.stopEvent();
6937         }
6938     },
6939      /**
6940      * Clear any invalid styles/messages for this field
6941      */
6942     clearInvalid : function(){
6943         
6944         if(!this.el || this.preventMark){ // not rendered
6945             return;
6946         }
6947         this.el.removeClass(this.invalidClass);
6948         /*
6949         switch(this.msgTarget){
6950             case 'qtip':
6951                 this.el.dom.qtip = '';
6952                 break;
6953             case 'title':
6954                 this.el.dom.title = '';
6955                 break;
6956             case 'under':
6957                 if(this.errorEl){
6958                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
6959                 }
6960                 break;
6961             case 'side':
6962                 if(this.errorIcon){
6963                     this.errorIcon.dom.qtip = '';
6964                     this.errorIcon.hide();
6965                     this.un('resize', this.alignErrorIcon, this);
6966                 }
6967                 break;
6968             default:
6969                 var t = Roo.getDom(this.msgTarget);
6970                 t.innerHTML = '';
6971                 t.style.display = 'none';
6972                 break;
6973         }
6974         */
6975         this.fireEvent('valid', this);
6976     },
6977      /**
6978      * Mark this field as invalid
6979      * @param {String} msg The validation message
6980      */
6981     markInvalid : function(msg){
6982         if(!this.el  || this.preventMark){ // not rendered
6983             return;
6984         }
6985         this.el.addClass(this.invalidClass);
6986         /*
6987         msg = msg || this.invalidText;
6988         switch(this.msgTarget){
6989             case 'qtip':
6990                 this.el.dom.qtip = msg;
6991                 this.el.dom.qclass = 'x-form-invalid-tip';
6992                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
6993                     Roo.QuickTips.enable();
6994                 }
6995                 break;
6996             case 'title':
6997                 this.el.dom.title = msg;
6998                 break;
6999             case 'under':
7000                 if(!this.errorEl){
7001                     var elp = this.el.findParent('.x-form-element', 5, true);
7002                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7003                     this.errorEl.setWidth(elp.getWidth(true)-20);
7004                 }
7005                 this.errorEl.update(msg);
7006                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7007                 break;
7008             case 'side':
7009                 if(!this.errorIcon){
7010                     var elp = this.el.findParent('.x-form-element', 5, true);
7011                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7012                 }
7013                 this.alignErrorIcon();
7014                 this.errorIcon.dom.qtip = msg;
7015                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7016                 this.errorIcon.show();
7017                 this.on('resize', this.alignErrorIcon, this);
7018                 break;
7019             default:
7020                 var t = Roo.getDom(this.msgTarget);
7021                 t.innerHTML = msg;
7022                 t.style.display = this.msgDisplay;
7023                 break;
7024         }
7025         */
7026         this.fireEvent('invalid', this, msg);
7027     },
7028     // private
7029     SafariOnKeyDown : function(event)
7030     {
7031         // this is a workaround for a password hang bug on chrome/ webkit.
7032         
7033         var isSelectAll = false;
7034         
7035         if(this.inputEl().dom.selectionEnd > 0){
7036             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7037         }
7038         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7039             event.preventDefault();
7040             this.setValue('');
7041             return;
7042         }
7043         
7044         if(isSelectAll){ // backspace and delete key
7045             
7046             event.preventDefault();
7047             // this is very hacky as keydown always get's upper case.
7048             //
7049             var cc = String.fromCharCode(event.getCharCode());
7050             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7051             
7052         }
7053     },
7054     adjustWidth : function(tag, w){
7055         tag = tag.toLowerCase();
7056         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7057             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7058                 if(tag == 'input'){
7059                     return w + 2;
7060                 }
7061                 if(tag == 'textarea'){
7062                     return w-2;
7063                 }
7064             }else if(Roo.isOpera){
7065                 if(tag == 'input'){
7066                     return w + 2;
7067                 }
7068                 if(tag == 'textarea'){
7069                     return w-2;
7070                 }
7071             }
7072         }
7073         return w;
7074     }
7075     
7076 });
7077
7078  
7079 /*
7080  * - LGPL
7081  *
7082  * Input
7083  * 
7084  */
7085
7086 /**
7087  * @class Roo.bootstrap.TextArea
7088  * @extends Roo.bootstrap.Input
7089  * Bootstrap TextArea class
7090  * @cfg {Number} cols Specifies the visible width of a text area
7091  * @cfg {Number} rows Specifies the visible number of lines in a text area
7092  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7093  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7094  * @cfg {string} html text
7095  * 
7096  * @constructor
7097  * Create a new TextArea
7098  * @param {Object} config The config object
7099  */
7100
7101 Roo.bootstrap.TextArea = function(config){
7102     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7103    
7104 };
7105
7106 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7107      
7108     cols : false,
7109     rows : 5,
7110     readOnly : false,
7111     warp : 'soft',
7112     resize : false,
7113     value: false,
7114     html: false,
7115     
7116     getAutoCreate : function(){
7117         
7118         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7119         
7120         var id = Roo.id();
7121         
7122         var cfg = {};
7123         
7124         var input =  {
7125             tag: 'textarea',
7126             id : id,
7127             warp : this.warp,
7128             rows : this.rows,
7129             value : this.value || '',
7130             html: this.html || '',
7131             cls : 'form-control',
7132             placeholder : this.placeholder || '' 
7133             
7134         };
7135         
7136         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7137             input.maxLength = this.maxLength;
7138         }
7139         
7140         if(this.resize){
7141             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7142         }
7143         
7144         if(this.cols){
7145             input.cols = this.cols;
7146         }
7147         
7148         if (this.readOnly) {
7149             input.readonly = true;
7150         }
7151         
7152         if (this.name) {
7153             input.name = this.name;
7154         }
7155         
7156         if (this.size) {
7157             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7158         }
7159         
7160         var settings=this;
7161         ['xs','sm','md','lg'].map(function(size){
7162             if (settings[size]) {
7163                 cfg.cls += ' col-' + size + '-' + settings[size];
7164             }
7165         });
7166         
7167         var inputblock = input;
7168         
7169         if (this.before || this.after) {
7170             
7171             inputblock = {
7172                 cls : 'input-group',
7173                 cn :  [] 
7174             };
7175             if (this.before) {
7176                 inputblock.cn.push({
7177                     tag :'span',
7178                     cls : 'input-group-addon',
7179                     html : this.before
7180                 });
7181             }
7182             inputblock.cn.push(input);
7183             if (this.after) {
7184                 inputblock.cn.push({
7185                     tag :'span',
7186                     cls : 'input-group-addon',
7187                     html : this.after
7188                 });
7189             }
7190             
7191         }
7192         
7193         if (align ==='left' && this.fieldLabel.length) {
7194                 Roo.log("left and has label");
7195                 cfg.cn = [
7196                     
7197                     {
7198                         tag: 'label',
7199                         'for' :  id,
7200                         cls : 'control-label col-sm-' + this.labelWidth,
7201                         html : this.fieldLabel
7202                         
7203                     },
7204                     {
7205                         cls : "col-sm-" + (12 - this.labelWidth), 
7206                         cn: [
7207                             inputblock
7208                         ]
7209                     }
7210                     
7211                 ];
7212         } else if ( this.fieldLabel.length) {
7213                 Roo.log(" label");
7214                  cfg.cn = [
7215                    
7216                     {
7217                         tag: 'label',
7218                         //cls : 'input-group-addon',
7219                         html : this.fieldLabel
7220                         
7221                     },
7222                     
7223                     inputblock
7224                     
7225                 ];
7226
7227         } else {
7228             
7229                    Roo.log(" no label && no align");
7230                 cfg.cn = [
7231                     
7232                         inputblock
7233                     
7234                 ];
7235                 
7236                 
7237         }
7238         
7239         if (this.disabled) {
7240             input.disabled=true;
7241         }
7242         
7243         return cfg;
7244         
7245     },
7246     /**
7247      * return the real textarea element.
7248      */
7249     inputEl: function ()
7250     {
7251         return this.el.select('textarea.form-control',true).first();
7252     }
7253 });
7254
7255  
7256 /*
7257  * - LGPL
7258  *
7259  * trigger field - base class for combo..
7260  * 
7261  */
7262  
7263 /**
7264  * @class Roo.bootstrap.TriggerField
7265  * @extends Roo.bootstrap.Input
7266  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7267  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7268  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7269  * for which you can provide a custom implementation.  For example:
7270  * <pre><code>
7271 var trigger = new Roo.bootstrap.TriggerField();
7272 trigger.onTriggerClick = myTriggerFn;
7273 trigger.applyTo('my-field');
7274 </code></pre>
7275  *
7276  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7277  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7278  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7279  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7280  * @constructor
7281  * Create a new TriggerField.
7282  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7283  * to the base TextField)
7284  */
7285 Roo.bootstrap.TriggerField = function(config){
7286     this.mimicing = false;
7287     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7288 };
7289
7290 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7291     /**
7292      * @cfg {String} triggerClass A CSS class to apply to the trigger
7293      */
7294      /**
7295      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7296      */
7297     hideTrigger:false,
7298
7299     /** @cfg {Boolean} grow @hide */
7300     /** @cfg {Number} growMin @hide */
7301     /** @cfg {Number} growMax @hide */
7302
7303     /**
7304      * @hide 
7305      * @method
7306      */
7307     autoSize: Roo.emptyFn,
7308     // private
7309     monitorTab : true,
7310     // private
7311     deferHeight : true,
7312
7313     
7314     actionMode : 'wrap',
7315     
7316     
7317     
7318     getAutoCreate : function(){
7319        
7320         var parent = this.parent();
7321         
7322         var align = this.labelAlign || this.parentLabelAlign();
7323         
7324         var id = Roo.id();
7325         
7326         var cfg = {
7327             cls: 'form-group' //input-group
7328         };
7329         
7330         
7331         var input =  {
7332             tag: 'input',
7333             id : id,
7334             type : this.inputType,
7335             cls : 'form-control',
7336             autocomplete: 'off',
7337             placeholder : this.placeholder || '' 
7338             
7339         };
7340         if (this.name) {
7341             input.name = this.name;
7342         }
7343         if (this.size) {
7344             input.cls += ' input-' + this.size;
7345         }
7346         
7347         if (this.disabled) {
7348             input.disabled=true;
7349         }
7350         
7351         var inputblock = input;
7352         
7353         if (this.before || this.after) {
7354             
7355             inputblock = {
7356                 cls : 'input-group',
7357                 cn :  [] 
7358             };
7359             if (this.before) {
7360                 inputblock.cn.push({
7361                     tag :'span',
7362                     cls : 'input-group-addon',
7363                     html : this.before
7364                 });
7365             }
7366             inputblock.cn.push(input);
7367             if (this.after) {
7368                 inputblock.cn.push({
7369                     tag :'span',
7370                     cls : 'input-group-addon',
7371                     html : this.after
7372                 });
7373             }
7374             
7375         };
7376         
7377         var box = {
7378             tag: 'div',
7379             cn: [
7380                 {
7381                     tag: 'input',
7382                     type : 'hidden',
7383                     cls: 'form-hidden-field'
7384                 },
7385                 inputblock
7386             ]
7387             
7388         };
7389         
7390         if(this.multiple){
7391             Roo.log('multiple');
7392             
7393             box = {
7394                 tag: 'div',
7395                 cn: [
7396                     {
7397                         tag: 'input',
7398                         type : 'hidden',
7399                         cls: 'form-hidden-field'
7400                     },
7401                     {
7402                         tag: 'ul',
7403                         cls: 'select2-choices',
7404                         cn:[
7405                             {
7406                                 tag: 'li',
7407                                 cls: 'select2-search-field',
7408                                 cn: [
7409
7410                                     inputblock
7411                                 ]
7412                             }
7413                         ]
7414                     }
7415                 ]
7416             }
7417         };
7418         
7419         var combobox = {
7420             cls: 'select2-container input-group',
7421             cn: [
7422                 box,
7423                 {
7424                     tag: 'ul',
7425                     cls: 'typeahead typeahead-long dropdown-menu',
7426                     style: 'display:none'
7427                 }
7428             ]
7429         };
7430         
7431         if(!this.multiple){
7432             combobox.cn.push({
7433                 tag :'span',
7434                 cls : 'input-group-addon btn dropdown-toggle',
7435                 cn : [
7436                     {
7437                         tag: 'span',
7438                         cls: 'caret'
7439                     },
7440                     {
7441                         tag: 'span',
7442                         cls: 'combobox-clear',
7443                         cn  : [
7444                             {
7445                                 tag : 'i',
7446                                 cls: 'icon-remove'
7447                             }
7448                         ]
7449                     }
7450                 ]
7451
7452             })
7453         }
7454         
7455         if(this.multiple){
7456             combobox.cls += ' select2-container-multi';
7457         }
7458         
7459         if (align ==='left' && this.fieldLabel.length) {
7460             
7461                 Roo.log("left and has label");
7462                 cfg.cn = [
7463                     
7464                     {
7465                         tag: 'label',
7466                         'for' :  id,
7467                         cls : 'control-label col-sm-' + this.labelWidth,
7468                         html : this.fieldLabel
7469                         
7470                     },
7471                     {
7472                         cls : "col-sm-" + (12 - this.labelWidth), 
7473                         cn: [
7474                             combobox
7475                         ]
7476                     }
7477                     
7478                 ];
7479         } else if ( this.fieldLabel.length) {
7480                 Roo.log(" label");
7481                  cfg.cn = [
7482                    
7483                     {
7484                         tag: 'label',
7485                         //cls : 'input-group-addon',
7486                         html : this.fieldLabel
7487                         
7488                     },
7489                     
7490                     combobox
7491                     
7492                 ];
7493
7494         } else {
7495             
7496                 Roo.log(" no label && no align");
7497                 cfg = combobox
7498                      
7499                 
7500         }
7501          
7502         var settings=this;
7503         ['xs','sm','md','lg'].map(function(size){
7504             if (settings[size]) {
7505                 cfg.cls += ' col-' + size + '-' + settings[size];
7506             }
7507         });
7508         
7509         return cfg;
7510         
7511     },
7512     
7513     
7514     
7515     // private
7516     onResize : function(w, h){
7517 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7518 //        if(typeof w == 'number'){
7519 //            var x = w - this.trigger.getWidth();
7520 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7521 //            this.trigger.setStyle('left', x+'px');
7522 //        }
7523     },
7524
7525     // private
7526     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7527
7528     // private
7529     getResizeEl : function(){
7530         return this.inputEl();
7531     },
7532
7533     // private
7534     getPositionEl : function(){
7535         return this.inputEl();
7536     },
7537
7538     // private
7539     alignErrorIcon : function(){
7540         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7541     },
7542
7543     // private
7544     initEvents : function(){
7545         
7546         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7547         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7548         if(!this.multiple){
7549             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7550             if(this.hideTrigger){
7551                 this.trigger.setDisplayed(false);
7552             }
7553             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7554         }
7555         
7556         if(this.multiple){
7557             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7558         }
7559         
7560         //this.trigger.addClassOnOver('x-form-trigger-over');
7561         //this.trigger.addClassOnClick('x-form-trigger-click');
7562         
7563         //if(!this.width){
7564         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7565         //}
7566     },
7567
7568     // private
7569     initTrigger : function(){
7570        
7571     },
7572
7573     // private
7574     onDestroy : function(){
7575         if(this.trigger){
7576             this.trigger.removeAllListeners();
7577           //  this.trigger.remove();
7578         }
7579         //if(this.wrap){
7580         //    this.wrap.remove();
7581         //}
7582         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
7583     },
7584
7585     // private
7586     onFocus : function(){
7587         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
7588         /*
7589         if(!this.mimicing){
7590             this.wrap.addClass('x-trigger-wrap-focus');
7591             this.mimicing = true;
7592             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
7593             if(this.monitorTab){
7594                 this.el.on("keydown", this.checkTab, this);
7595             }
7596         }
7597         */
7598     },
7599
7600     // private
7601     checkTab : function(e){
7602         if(e.getKey() == e.TAB){
7603             this.triggerBlur();
7604         }
7605     },
7606
7607     // private
7608     onBlur : function(){
7609         // do nothing
7610     },
7611
7612     // private
7613     mimicBlur : function(e, t){
7614         /*
7615         if(!this.wrap.contains(t) && this.validateBlur()){
7616             this.triggerBlur();
7617         }
7618         */
7619     },
7620
7621     // private
7622     triggerBlur : function(){
7623         this.mimicing = false;
7624         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7625         if(this.monitorTab){
7626             this.el.un("keydown", this.checkTab, this);
7627         }
7628         //this.wrap.removeClass('x-trigger-wrap-focus');
7629         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7630     },
7631
7632     // private
7633     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7634     validateBlur : function(e, t){
7635         return true;
7636     },
7637
7638     // private
7639     onDisable : function(){
7640         this.inputEl().dom.disabled = true;
7641         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7642         //if(this.wrap){
7643         //    this.wrap.addClass('x-item-disabled');
7644         //}
7645     },
7646
7647     // private
7648     onEnable : function(){
7649         this.inputEl().dom.disabled = false;
7650         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
7651         //if(this.wrap){
7652         //    this.el.removeClass('x-item-disabled');
7653         //}
7654     },
7655
7656     // private
7657     onShow : function(){
7658         var ae = this.getActionEl();
7659         
7660         if(ae){
7661             ae.dom.style.display = '';
7662             ae.dom.style.visibility = 'visible';
7663         }
7664     },
7665
7666     // private
7667     
7668     onHide : function(){
7669         var ae = this.getActionEl();
7670         ae.dom.style.display = 'none';
7671     },
7672
7673     /**
7674      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
7675      * by an implementing function.
7676      * @method
7677      * @param {EventObject} e
7678      */
7679     onTriggerClick : Roo.emptyFn
7680 });
7681  /*
7682  * Based on:
7683  * Ext JS Library 1.1.1
7684  * Copyright(c) 2006-2007, Ext JS, LLC.
7685  *
7686  * Originally Released Under LGPL - original licence link has changed is not relivant.
7687  *
7688  * Fork - LGPL
7689  * <script type="text/javascript">
7690  */
7691
7692
7693 /**
7694  * @class Roo.data.SortTypes
7695  * @singleton
7696  * Defines the default sorting (casting?) comparison functions used when sorting data.
7697  */
7698 Roo.data.SortTypes = {
7699     /**
7700      * Default sort that does nothing
7701      * @param {Mixed} s The value being converted
7702      * @return {Mixed} The comparison value
7703      */
7704     none : function(s){
7705         return s;
7706     },
7707     
7708     /**
7709      * The regular expression used to strip tags
7710      * @type {RegExp}
7711      * @property
7712      */
7713     stripTagsRE : /<\/?[^>]+>/gi,
7714     
7715     /**
7716      * Strips all HTML tags to sort on text only
7717      * @param {Mixed} s The value being converted
7718      * @return {String} The comparison value
7719      */
7720     asText : function(s){
7721         return String(s).replace(this.stripTagsRE, "");
7722     },
7723     
7724     /**
7725      * Strips all HTML tags to sort on text only - Case insensitive
7726      * @param {Mixed} s The value being converted
7727      * @return {String} The comparison value
7728      */
7729     asUCText : function(s){
7730         return String(s).toUpperCase().replace(this.stripTagsRE, "");
7731     },
7732     
7733     /**
7734      * Case insensitive string
7735      * @param {Mixed} s The value being converted
7736      * @return {String} The comparison value
7737      */
7738     asUCString : function(s) {
7739         return String(s).toUpperCase();
7740     },
7741     
7742     /**
7743      * Date sorting
7744      * @param {Mixed} s The value being converted
7745      * @return {Number} The comparison value
7746      */
7747     asDate : function(s) {
7748         if(!s){
7749             return 0;
7750         }
7751         if(s instanceof Date){
7752             return s.getTime();
7753         }
7754         return Date.parse(String(s));
7755     },
7756     
7757     /**
7758      * Float sorting
7759      * @param {Mixed} s The value being converted
7760      * @return {Float} The comparison value
7761      */
7762     asFloat : function(s) {
7763         var val = parseFloat(String(s).replace(/,/g, ""));
7764         if(isNaN(val)) val = 0;
7765         return val;
7766     },
7767     
7768     /**
7769      * Integer sorting
7770      * @param {Mixed} s The value being converted
7771      * @return {Number} The comparison value
7772      */
7773     asInt : function(s) {
7774         var val = parseInt(String(s).replace(/,/g, ""));
7775         if(isNaN(val)) val = 0;
7776         return val;
7777     }
7778 };/*
7779  * Based on:
7780  * Ext JS Library 1.1.1
7781  * Copyright(c) 2006-2007, Ext JS, LLC.
7782  *
7783  * Originally Released Under LGPL - original licence link has changed is not relivant.
7784  *
7785  * Fork - LGPL
7786  * <script type="text/javascript">
7787  */
7788
7789 /**
7790 * @class Roo.data.Record
7791  * Instances of this class encapsulate both record <em>definition</em> information, and record
7792  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
7793  * to access Records cached in an {@link Roo.data.Store} object.<br>
7794  * <p>
7795  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
7796  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
7797  * objects.<br>
7798  * <p>
7799  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
7800  * @constructor
7801  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
7802  * {@link #create}. The parameters are the same.
7803  * @param {Array} data An associative Array of data values keyed by the field name.
7804  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
7805  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
7806  * not specified an integer id is generated.
7807  */
7808 Roo.data.Record = function(data, id){
7809     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
7810     this.data = data;
7811 };
7812
7813 /**
7814  * Generate a constructor for a specific record layout.
7815  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
7816  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
7817  * Each field definition object may contain the following properties: <ul>
7818  * <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,
7819  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
7820  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
7821  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
7822  * is being used, then this is a string containing the javascript expression to reference the data relative to 
7823  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
7824  * to the data item relative to the record element. If the mapping expression is the same as the field name,
7825  * this may be omitted.</p></li>
7826  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
7827  * <ul><li>auto (Default, implies no conversion)</li>
7828  * <li>string</li>
7829  * <li>int</li>
7830  * <li>float</li>
7831  * <li>boolean</li>
7832  * <li>date</li></ul></p></li>
7833  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
7834  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
7835  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
7836  * by the Reader into an object that will be stored in the Record. It is passed the
7837  * following parameters:<ul>
7838  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
7839  * </ul></p></li>
7840  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
7841  * </ul>
7842  * <br>usage:<br><pre><code>
7843 var TopicRecord = Roo.data.Record.create(
7844     {name: 'title', mapping: 'topic_title'},
7845     {name: 'author', mapping: 'username'},
7846     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
7847     {name: 'lastPost', mapping: 'post_time', type: 'date'},
7848     {name: 'lastPoster', mapping: 'user2'},
7849     {name: 'excerpt', mapping: 'post_text'}
7850 );
7851
7852 var myNewRecord = new TopicRecord({
7853     title: 'Do my job please',
7854     author: 'noobie',
7855     totalPosts: 1,
7856     lastPost: new Date(),
7857     lastPoster: 'Animal',
7858     excerpt: 'No way dude!'
7859 });
7860 myStore.add(myNewRecord);
7861 </code></pre>
7862  * @method create
7863  * @static
7864  */
7865 Roo.data.Record.create = function(o){
7866     var f = function(){
7867         f.superclass.constructor.apply(this, arguments);
7868     };
7869     Roo.extend(f, Roo.data.Record);
7870     var p = f.prototype;
7871     p.fields = new Roo.util.MixedCollection(false, function(field){
7872         return field.name;
7873     });
7874     for(var i = 0, len = o.length; i < len; i++){
7875         p.fields.add(new Roo.data.Field(o[i]));
7876     }
7877     f.getField = function(name){
7878         return p.fields.get(name);  
7879     };
7880     return f;
7881 };
7882
7883 Roo.data.Record.AUTO_ID = 1000;
7884 Roo.data.Record.EDIT = 'edit';
7885 Roo.data.Record.REJECT = 'reject';
7886 Roo.data.Record.COMMIT = 'commit';
7887
7888 Roo.data.Record.prototype = {
7889     /**
7890      * Readonly flag - true if this record has been modified.
7891      * @type Boolean
7892      */
7893     dirty : false,
7894     editing : false,
7895     error: null,
7896     modified: null,
7897
7898     // private
7899     join : function(store){
7900         this.store = store;
7901     },
7902
7903     /**
7904      * Set the named field to the specified value.
7905      * @param {String} name The name of the field to set.
7906      * @param {Object} value The value to set the field to.
7907      */
7908     set : function(name, value){
7909         if(this.data[name] == value){
7910             return;
7911         }
7912         this.dirty = true;
7913         if(!this.modified){
7914             this.modified = {};
7915         }
7916         if(typeof this.modified[name] == 'undefined'){
7917             this.modified[name] = this.data[name];
7918         }
7919         this.data[name] = value;
7920         if(!this.editing && this.store){
7921             this.store.afterEdit(this);
7922         }       
7923     },
7924
7925     /**
7926      * Get the value of the named field.
7927      * @param {String} name The name of the field to get the value of.
7928      * @return {Object} The value of the field.
7929      */
7930     get : function(name){
7931         return this.data[name]; 
7932     },
7933
7934     // private
7935     beginEdit : function(){
7936         this.editing = true;
7937         this.modified = {}; 
7938     },
7939
7940     // private
7941     cancelEdit : function(){
7942         this.editing = false;
7943         delete this.modified;
7944     },
7945
7946     // private
7947     endEdit : function(){
7948         this.editing = false;
7949         if(this.dirty && this.store){
7950             this.store.afterEdit(this);
7951         }
7952     },
7953
7954     /**
7955      * Usually called by the {@link Roo.data.Store} which owns the Record.
7956      * Rejects all changes made to the Record since either creation, or the last commit operation.
7957      * Modified fields are reverted to their original values.
7958      * <p>
7959      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7960      * of reject operations.
7961      */
7962     reject : function(){
7963         var m = this.modified;
7964         for(var n in m){
7965             if(typeof m[n] != "function"){
7966                 this.data[n] = m[n];
7967             }
7968         }
7969         this.dirty = false;
7970         delete this.modified;
7971         this.editing = false;
7972         if(this.store){
7973             this.store.afterReject(this);
7974         }
7975     },
7976
7977     /**
7978      * Usually called by the {@link Roo.data.Store} which owns the Record.
7979      * Commits all changes made to the Record since either creation, or the last commit operation.
7980      * <p>
7981      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7982      * of commit operations.
7983      */
7984     commit : function(){
7985         this.dirty = false;
7986         delete this.modified;
7987         this.editing = false;
7988         if(this.store){
7989             this.store.afterCommit(this);
7990         }
7991     },
7992
7993     // private
7994     hasError : function(){
7995         return this.error != null;
7996     },
7997
7998     // private
7999     clearError : function(){
8000         this.error = null;
8001     },
8002
8003     /**
8004      * Creates a copy of this record.
8005      * @param {String} id (optional) A new record id if you don't want to use this record's id
8006      * @return {Record}
8007      */
8008     copy : function(newId) {
8009         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8010     }
8011 };/*
8012  * Based on:
8013  * Ext JS Library 1.1.1
8014  * Copyright(c) 2006-2007, Ext JS, LLC.
8015  *
8016  * Originally Released Under LGPL - original licence link has changed is not relivant.
8017  *
8018  * Fork - LGPL
8019  * <script type="text/javascript">
8020  */
8021
8022
8023
8024 /**
8025  * @class Roo.data.Store
8026  * @extends Roo.util.Observable
8027  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8028  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8029  * <p>
8030  * 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
8031  * has no knowledge of the format of the data returned by the Proxy.<br>
8032  * <p>
8033  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8034  * instances from the data object. These records are cached and made available through accessor functions.
8035  * @constructor
8036  * Creates a new Store.
8037  * @param {Object} config A config object containing the objects needed for the Store to access data,
8038  * and read the data into Records.
8039  */
8040 Roo.data.Store = function(config){
8041     this.data = new Roo.util.MixedCollection(false);
8042     this.data.getKey = function(o){
8043         return o.id;
8044     };
8045     this.baseParams = {};
8046     // private
8047     this.paramNames = {
8048         "start" : "start",
8049         "limit" : "limit",
8050         "sort" : "sort",
8051         "dir" : "dir",
8052         "multisort" : "_multisort"
8053     };
8054
8055     if(config && config.data){
8056         this.inlineData = config.data;
8057         delete config.data;
8058     }
8059
8060     Roo.apply(this, config);
8061     
8062     if(this.reader){ // reader passed
8063         this.reader = Roo.factory(this.reader, Roo.data);
8064         this.reader.xmodule = this.xmodule || false;
8065         if(!this.recordType){
8066             this.recordType = this.reader.recordType;
8067         }
8068         if(this.reader.onMetaChange){
8069             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8070         }
8071     }
8072
8073     if(this.recordType){
8074         this.fields = this.recordType.prototype.fields;
8075     }
8076     this.modified = [];
8077
8078     this.addEvents({
8079         /**
8080          * @event datachanged
8081          * Fires when the data cache has changed, and a widget which is using this Store
8082          * as a Record cache should refresh its view.
8083          * @param {Store} this
8084          */
8085         datachanged : true,
8086         /**
8087          * @event metachange
8088          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8089          * @param {Store} this
8090          * @param {Object} meta The JSON metadata
8091          */
8092         metachange : true,
8093         /**
8094          * @event add
8095          * Fires when Records have been added to the Store
8096          * @param {Store} this
8097          * @param {Roo.data.Record[]} records The array of Records added
8098          * @param {Number} index The index at which the record(s) were added
8099          */
8100         add : true,
8101         /**
8102          * @event remove
8103          * Fires when a Record has been removed from the Store
8104          * @param {Store} this
8105          * @param {Roo.data.Record} record The Record that was removed
8106          * @param {Number} index The index at which the record was removed
8107          */
8108         remove : true,
8109         /**
8110          * @event update
8111          * Fires when a Record has been updated
8112          * @param {Store} this
8113          * @param {Roo.data.Record} record The Record that was updated
8114          * @param {String} operation The update operation being performed.  Value may be one of:
8115          * <pre><code>
8116  Roo.data.Record.EDIT
8117  Roo.data.Record.REJECT
8118  Roo.data.Record.COMMIT
8119          * </code></pre>
8120          */
8121         update : true,
8122         /**
8123          * @event clear
8124          * Fires when the data cache has been cleared.
8125          * @param {Store} this
8126          */
8127         clear : true,
8128         /**
8129          * @event beforeload
8130          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8131          * the load action will be canceled.
8132          * @param {Store} this
8133          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8134          */
8135         beforeload : true,
8136         /**
8137          * @event beforeloadadd
8138          * Fires after a new set of Records has been loaded.
8139          * @param {Store} this
8140          * @param {Roo.data.Record[]} records The Records that were loaded
8141          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8142          */
8143         beforeloadadd : true,
8144         /**
8145          * @event load
8146          * Fires after a new set of Records has been loaded, before they are added to the store.
8147          * @param {Store} this
8148          * @param {Roo.data.Record[]} records The Records that were loaded
8149          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8150          * @params {Object} return from reader
8151          */
8152         load : true,
8153         /**
8154          * @event loadexception
8155          * Fires if an exception occurs in the Proxy during loading.
8156          * Called with the signature of the Proxy's "loadexception" event.
8157          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8158          * 
8159          * @param {Proxy} 
8160          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8161          * @param {Object} load options 
8162          * @param {Object} jsonData from your request (normally this contains the Exception)
8163          */
8164         loadexception : true
8165     });
8166     
8167     if(this.proxy){
8168         this.proxy = Roo.factory(this.proxy, Roo.data);
8169         this.proxy.xmodule = this.xmodule || false;
8170         this.relayEvents(this.proxy,  ["loadexception"]);
8171     }
8172     this.sortToggle = {};
8173     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8174
8175     Roo.data.Store.superclass.constructor.call(this);
8176
8177     if(this.inlineData){
8178         this.loadData(this.inlineData);
8179         delete this.inlineData;
8180     }
8181 };
8182
8183 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8184      /**
8185     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8186     * without a remote query - used by combo/forms at present.
8187     */
8188     
8189     /**
8190     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8191     */
8192     /**
8193     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8194     */
8195     /**
8196     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8197     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8198     */
8199     /**
8200     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8201     * on any HTTP request
8202     */
8203     /**
8204     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8205     */
8206     /**
8207     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8208     */
8209     multiSort: false,
8210     /**
8211     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8212     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8213     */
8214     remoteSort : false,
8215
8216     /**
8217     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8218      * loaded or when a record is removed. (defaults to false).
8219     */
8220     pruneModifiedRecords : false,
8221
8222     // private
8223     lastOptions : null,
8224
8225     /**
8226      * Add Records to the Store and fires the add event.
8227      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8228      */
8229     add : function(records){
8230         records = [].concat(records);
8231         for(var i = 0, len = records.length; i < len; i++){
8232             records[i].join(this);
8233         }
8234         var index = this.data.length;
8235         this.data.addAll(records);
8236         this.fireEvent("add", this, records, index);
8237     },
8238
8239     /**
8240      * Remove a Record from the Store and fires the remove event.
8241      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8242      */
8243     remove : function(record){
8244         var index = this.data.indexOf(record);
8245         this.data.removeAt(index);
8246         if(this.pruneModifiedRecords){
8247             this.modified.remove(record);
8248         }
8249         this.fireEvent("remove", this, record, index);
8250     },
8251
8252     /**
8253      * Remove all Records from the Store and fires the clear event.
8254      */
8255     removeAll : function(){
8256         this.data.clear();
8257         if(this.pruneModifiedRecords){
8258             this.modified = [];
8259         }
8260         this.fireEvent("clear", this);
8261     },
8262
8263     /**
8264      * Inserts Records to the Store at the given index and fires the add event.
8265      * @param {Number} index The start index at which to insert the passed Records.
8266      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8267      */
8268     insert : function(index, records){
8269         records = [].concat(records);
8270         for(var i = 0, len = records.length; i < len; i++){
8271             this.data.insert(index, records[i]);
8272             records[i].join(this);
8273         }
8274         this.fireEvent("add", this, records, index);
8275     },
8276
8277     /**
8278      * Get the index within the cache of the passed Record.
8279      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8280      * @return {Number} The index of the passed Record. Returns -1 if not found.
8281      */
8282     indexOf : function(record){
8283         return this.data.indexOf(record);
8284     },
8285
8286     /**
8287      * Get the index within the cache of the Record with the passed id.
8288      * @param {String} id The id of the Record to find.
8289      * @return {Number} The index of the Record. Returns -1 if not found.
8290      */
8291     indexOfId : function(id){
8292         return this.data.indexOfKey(id);
8293     },
8294
8295     /**
8296      * Get the Record with the specified id.
8297      * @param {String} id The id of the Record to find.
8298      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8299      */
8300     getById : function(id){
8301         return this.data.key(id);
8302     },
8303
8304     /**
8305      * Get the Record at the specified index.
8306      * @param {Number} index The index of the Record to find.
8307      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8308      */
8309     getAt : function(index){
8310         return this.data.itemAt(index);
8311     },
8312
8313     /**
8314      * Returns a range of Records between specified indices.
8315      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8316      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8317      * @return {Roo.data.Record[]} An array of Records
8318      */
8319     getRange : function(start, end){
8320         return this.data.getRange(start, end);
8321     },
8322
8323     // private
8324     storeOptions : function(o){
8325         o = Roo.apply({}, o);
8326         delete o.callback;
8327         delete o.scope;
8328         this.lastOptions = o;
8329     },
8330
8331     /**
8332      * Loads the Record cache from the configured Proxy using the configured Reader.
8333      * <p>
8334      * If using remote paging, then the first load call must specify the <em>start</em>
8335      * and <em>limit</em> properties in the options.params property to establish the initial
8336      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8337      * <p>
8338      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8339      * and this call will return before the new data has been loaded. Perform any post-processing
8340      * in a callback function, or in a "load" event handler.</strong>
8341      * <p>
8342      * @param {Object} options An object containing properties which control loading options:<ul>
8343      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8344      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8345      * passed the following arguments:<ul>
8346      * <li>r : Roo.data.Record[]</li>
8347      * <li>options: Options object from the load call</li>
8348      * <li>success: Boolean success indicator</li></ul></li>
8349      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8350      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8351      * </ul>
8352      */
8353     load : function(options){
8354         options = options || {};
8355         if(this.fireEvent("beforeload", this, options) !== false){
8356             this.storeOptions(options);
8357             var p = Roo.apply(options.params || {}, this.baseParams);
8358             // if meta was not loaded from remote source.. try requesting it.
8359             if (!this.reader.metaFromRemote) {
8360                 p._requestMeta = 1;
8361             }
8362             if(this.sortInfo && this.remoteSort){
8363                 var pn = this.paramNames;
8364                 p[pn["sort"]] = this.sortInfo.field;
8365                 p[pn["dir"]] = this.sortInfo.direction;
8366             }
8367             if (this.multiSort) {
8368                 var pn = this.paramNames;
8369                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8370             }
8371             
8372             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8373         }
8374     },
8375
8376     /**
8377      * Reloads the Record cache from the configured Proxy using the configured Reader and
8378      * the options from the last load operation performed.
8379      * @param {Object} options (optional) An object containing properties which may override the options
8380      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8381      * the most recently used options are reused).
8382      */
8383     reload : function(options){
8384         this.load(Roo.applyIf(options||{}, this.lastOptions));
8385     },
8386
8387     // private
8388     // Called as a callback by the Reader during a load operation.
8389     loadRecords : function(o, options, success){
8390         if(!o || success === false){
8391             if(success !== false){
8392                 this.fireEvent("load", this, [], options, o);
8393             }
8394             if(options.callback){
8395                 options.callback.call(options.scope || this, [], options, false);
8396             }
8397             return;
8398         }
8399         // if data returned failure - throw an exception.
8400         if (o.success === false) {
8401             // show a message if no listener is registered.
8402             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8403                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8404             }
8405             // loadmask wil be hooked into this..
8406             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8407             return;
8408         }
8409         var r = o.records, t = o.totalRecords || r.length;
8410         
8411         this.fireEvent("beforeloadadd", this, r, options, o);
8412         
8413         if(!options || options.add !== true){
8414             if(this.pruneModifiedRecords){
8415                 this.modified = [];
8416             }
8417             for(var i = 0, len = r.length; i < len; i++){
8418                 r[i].join(this);
8419             }
8420             if(this.snapshot){
8421                 this.data = this.snapshot;
8422                 delete this.snapshot;
8423             }
8424             this.data.clear();
8425             this.data.addAll(r);
8426             this.totalLength = t;
8427             this.applySort();
8428             this.fireEvent("datachanged", this);
8429         }else{
8430             this.totalLength = Math.max(t, this.data.length+r.length);
8431             this.add(r);
8432         }
8433         this.fireEvent("load", this, r, options, o);
8434         if(options.callback){
8435             options.callback.call(options.scope || this, r, options, true);
8436         }
8437     },
8438
8439
8440     /**
8441      * Loads data from a passed data block. A Reader which understands the format of the data
8442      * must have been configured in the constructor.
8443      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8444      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8445      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8446      */
8447     loadData : function(o, append){
8448         var r = this.reader.readRecords(o);
8449         this.loadRecords(r, {add: append}, true);
8450     },
8451
8452     /**
8453      * Gets the number of cached records.
8454      * <p>
8455      * <em>If using paging, this may not be the total size of the dataset. If the data object
8456      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8457      * the data set size</em>
8458      */
8459     getCount : function(){
8460         return this.data.length || 0;
8461     },
8462
8463     /**
8464      * Gets the total number of records in the dataset as returned by the server.
8465      * <p>
8466      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8467      * the dataset size</em>
8468      */
8469     getTotalCount : function(){
8470         return this.totalLength || 0;
8471     },
8472
8473     /**
8474      * Returns the sort state of the Store as an object with two properties:
8475      * <pre><code>
8476  field {String} The name of the field by which the Records are sorted
8477  direction {String} The sort order, "ASC" or "DESC"
8478      * </code></pre>
8479      */
8480     getSortState : function(){
8481         return this.sortInfo;
8482     },
8483
8484     // private
8485     applySort : function(){
8486         if(this.sortInfo && !this.remoteSort){
8487             var s = this.sortInfo, f = s.field;
8488             var st = this.fields.get(f).sortType;
8489             var fn = function(r1, r2){
8490                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8491                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8492             };
8493             this.data.sort(s.direction, fn);
8494             if(this.snapshot && this.snapshot != this.data){
8495                 this.snapshot.sort(s.direction, fn);
8496             }
8497         }
8498     },
8499
8500     /**
8501      * Sets the default sort column and order to be used by the next load operation.
8502      * @param {String} fieldName The name of the field to sort by.
8503      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8504      */
8505     setDefaultSort : function(field, dir){
8506         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8507     },
8508
8509     /**
8510      * Sort the Records.
8511      * If remote sorting is used, the sort is performed on the server, and the cache is
8512      * reloaded. If local sorting is used, the cache is sorted internally.
8513      * @param {String} fieldName The name of the field to sort by.
8514      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8515      */
8516     sort : function(fieldName, dir){
8517         var f = this.fields.get(fieldName);
8518         if(!dir){
8519             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8520             
8521             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8522                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8523             }else{
8524                 dir = f.sortDir;
8525             }
8526         }
8527         this.sortToggle[f.name] = dir;
8528         this.sortInfo = {field: f.name, direction: dir};
8529         if(!this.remoteSort){
8530             this.applySort();
8531             this.fireEvent("datachanged", this);
8532         }else{
8533             this.load(this.lastOptions);
8534         }
8535     },
8536
8537     /**
8538      * Calls the specified function for each of the Records in the cache.
8539      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8540      * Returning <em>false</em> aborts and exits the iteration.
8541      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8542      */
8543     each : function(fn, scope){
8544         this.data.each(fn, scope);
8545     },
8546
8547     /**
8548      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8549      * (e.g., during paging).
8550      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8551      */
8552     getModifiedRecords : function(){
8553         return this.modified;
8554     },
8555
8556     // private
8557     createFilterFn : function(property, value, anyMatch){
8558         if(!value.exec){ // not a regex
8559             value = String(value);
8560             if(value.length == 0){
8561                 return false;
8562             }
8563             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8564         }
8565         return function(r){
8566             return value.test(r.data[property]);
8567         };
8568     },
8569
8570     /**
8571      * Sums the value of <i>property</i> for each record between start and end and returns the result.
8572      * @param {String} property A field on your records
8573      * @param {Number} start The record index to start at (defaults to 0)
8574      * @param {Number} end The last record index to include (defaults to length - 1)
8575      * @return {Number} The sum
8576      */
8577     sum : function(property, start, end){
8578         var rs = this.data.items, v = 0;
8579         start = start || 0;
8580         end = (end || end === 0) ? end : rs.length-1;
8581
8582         for(var i = start; i <= end; i++){
8583             v += (rs[i].data[property] || 0);
8584         }
8585         return v;
8586     },
8587
8588     /**
8589      * Filter the records by a specified property.
8590      * @param {String} field A field on your records
8591      * @param {String/RegExp} value Either a string that the field
8592      * should start with or a RegExp to test against the field
8593      * @param {Boolean} anyMatch True to match any part not just the beginning
8594      */
8595     filter : function(property, value, anyMatch){
8596         var fn = this.createFilterFn(property, value, anyMatch);
8597         return fn ? this.filterBy(fn) : this.clearFilter();
8598     },
8599
8600     /**
8601      * Filter by a function. The specified function will be called with each
8602      * record in this data source. If the function returns true the record is included,
8603      * otherwise it is filtered.
8604      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8605      * @param {Object} scope (optional) The scope of the function (defaults to this)
8606      */
8607     filterBy : function(fn, scope){
8608         this.snapshot = this.snapshot || this.data;
8609         this.data = this.queryBy(fn, scope||this);
8610         this.fireEvent("datachanged", this);
8611     },
8612
8613     /**
8614      * Query the records by a specified property.
8615      * @param {String} field A field on your records
8616      * @param {String/RegExp} value Either a string that the field
8617      * should start with or a RegExp to test against the field
8618      * @param {Boolean} anyMatch True to match any part not just the beginning
8619      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8620      */
8621     query : function(property, value, anyMatch){
8622         var fn = this.createFilterFn(property, value, anyMatch);
8623         return fn ? this.queryBy(fn) : this.data.clone();
8624     },
8625
8626     /**
8627      * Query by a function. The specified function will be called with each
8628      * record in this data source. If the function returns true the record is included
8629      * in the results.
8630      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8631      * @param {Object} scope (optional) The scope of the function (defaults to this)
8632       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8633      **/
8634     queryBy : function(fn, scope){
8635         var data = this.snapshot || this.data;
8636         return data.filterBy(fn, scope||this);
8637     },
8638
8639     /**
8640      * Collects unique values for a particular dataIndex from this store.
8641      * @param {String} dataIndex The property to collect
8642      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8643      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8644      * @return {Array} An array of the unique values
8645      **/
8646     collect : function(dataIndex, allowNull, bypassFilter){
8647         var d = (bypassFilter === true && this.snapshot) ?
8648                 this.snapshot.items : this.data.items;
8649         var v, sv, r = [], l = {};
8650         for(var i = 0, len = d.length; i < len; i++){
8651             v = d[i].data[dataIndex];
8652             sv = String(v);
8653             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
8654                 l[sv] = true;
8655                 r[r.length] = v;
8656             }
8657         }
8658         return r;
8659     },
8660
8661     /**
8662      * Revert to a view of the Record cache with no filtering applied.
8663      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
8664      */
8665     clearFilter : function(suppressEvent){
8666         if(this.snapshot && this.snapshot != this.data){
8667             this.data = this.snapshot;
8668             delete this.snapshot;
8669             if(suppressEvent !== true){
8670                 this.fireEvent("datachanged", this);
8671             }
8672         }
8673     },
8674
8675     // private
8676     afterEdit : function(record){
8677         if(this.modified.indexOf(record) == -1){
8678             this.modified.push(record);
8679         }
8680         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
8681     },
8682     
8683     // private
8684     afterReject : function(record){
8685         this.modified.remove(record);
8686         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
8687     },
8688
8689     // private
8690     afterCommit : function(record){
8691         this.modified.remove(record);
8692         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
8693     },
8694
8695     /**
8696      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
8697      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
8698      */
8699     commitChanges : function(){
8700         var m = this.modified.slice(0);
8701         this.modified = [];
8702         for(var i = 0, len = m.length; i < len; i++){
8703             m[i].commit();
8704         }
8705     },
8706
8707     /**
8708      * Cancel outstanding changes on all changed records.
8709      */
8710     rejectChanges : function(){
8711         var m = this.modified.slice(0);
8712         this.modified = [];
8713         for(var i = 0, len = m.length; i < len; i++){
8714             m[i].reject();
8715         }
8716     },
8717
8718     onMetaChange : function(meta, rtype, o){
8719         this.recordType = rtype;
8720         this.fields = rtype.prototype.fields;
8721         delete this.snapshot;
8722         this.sortInfo = meta.sortInfo || this.sortInfo;
8723         this.modified = [];
8724         this.fireEvent('metachange', this, this.reader.meta);
8725     },
8726     
8727     moveIndex : function(data, type)
8728     {
8729         var index = this.indexOf(data);
8730         
8731         var newIndex = index + type;
8732         
8733         this.remove(data);
8734         
8735         this.insert(newIndex, data);
8736         
8737     }
8738 });/*
8739  * Based on:
8740  * Ext JS Library 1.1.1
8741  * Copyright(c) 2006-2007, Ext JS, LLC.
8742  *
8743  * Originally Released Under LGPL - original licence link has changed is not relivant.
8744  *
8745  * Fork - LGPL
8746  * <script type="text/javascript">
8747  */
8748
8749 /**
8750  * @class Roo.data.SimpleStore
8751  * @extends Roo.data.Store
8752  * Small helper class to make creating Stores from Array data easier.
8753  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
8754  * @cfg {Array} fields An array of field definition objects, or field name strings.
8755  * @cfg {Array} data The multi-dimensional array of data
8756  * @constructor
8757  * @param {Object} config
8758  */
8759 Roo.data.SimpleStore = function(config){
8760     Roo.data.SimpleStore.superclass.constructor.call(this, {
8761         isLocal : true,
8762         reader: new Roo.data.ArrayReader({
8763                 id: config.id
8764             },
8765             Roo.data.Record.create(config.fields)
8766         ),
8767         proxy : new Roo.data.MemoryProxy(config.data)
8768     });
8769     this.load();
8770 };
8771 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
8772  * Based on:
8773  * Ext JS Library 1.1.1
8774  * Copyright(c) 2006-2007, Ext JS, LLC.
8775  *
8776  * Originally Released Under LGPL - original licence link has changed is not relivant.
8777  *
8778  * Fork - LGPL
8779  * <script type="text/javascript">
8780  */
8781
8782 /**
8783 /**
8784  * @extends Roo.data.Store
8785  * @class Roo.data.JsonStore
8786  * Small helper class to make creating Stores for JSON data easier. <br/>
8787 <pre><code>
8788 var store = new Roo.data.JsonStore({
8789     url: 'get-images.php',
8790     root: 'images',
8791     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
8792 });
8793 </code></pre>
8794  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
8795  * JsonReader and HttpProxy (unless inline data is provided).</b>
8796  * @cfg {Array} fields An array of field definition objects, or field name strings.
8797  * @constructor
8798  * @param {Object} config
8799  */
8800 Roo.data.JsonStore = function(c){
8801     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
8802         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
8803         reader: new Roo.data.JsonReader(c, c.fields)
8804     }));
8805 };
8806 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
8807  * Based on:
8808  * Ext JS Library 1.1.1
8809  * Copyright(c) 2006-2007, Ext JS, LLC.
8810  *
8811  * Originally Released Under LGPL - original licence link has changed is not relivant.
8812  *
8813  * Fork - LGPL
8814  * <script type="text/javascript">
8815  */
8816
8817  
8818 Roo.data.Field = function(config){
8819     if(typeof config == "string"){
8820         config = {name: config};
8821     }
8822     Roo.apply(this, config);
8823     
8824     if(!this.type){
8825         this.type = "auto";
8826     }
8827     
8828     var st = Roo.data.SortTypes;
8829     // named sortTypes are supported, here we look them up
8830     if(typeof this.sortType == "string"){
8831         this.sortType = st[this.sortType];
8832     }
8833     
8834     // set default sortType for strings and dates
8835     if(!this.sortType){
8836         switch(this.type){
8837             case "string":
8838                 this.sortType = st.asUCString;
8839                 break;
8840             case "date":
8841                 this.sortType = st.asDate;
8842                 break;
8843             default:
8844                 this.sortType = st.none;
8845         }
8846     }
8847
8848     // define once
8849     var stripRe = /[\$,%]/g;
8850
8851     // prebuilt conversion function for this field, instead of
8852     // switching every time we're reading a value
8853     if(!this.convert){
8854         var cv, dateFormat = this.dateFormat;
8855         switch(this.type){
8856             case "":
8857             case "auto":
8858             case undefined:
8859                 cv = function(v){ return v; };
8860                 break;
8861             case "string":
8862                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
8863                 break;
8864             case "int":
8865                 cv = function(v){
8866                     return v !== undefined && v !== null && v !== '' ?
8867                            parseInt(String(v).replace(stripRe, ""), 10) : '';
8868                     };
8869                 break;
8870             case "float":
8871                 cv = function(v){
8872                     return v !== undefined && v !== null && v !== '' ?
8873                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
8874                     };
8875                 break;
8876             case "bool":
8877             case "boolean":
8878                 cv = function(v){ return v === true || v === "true" || v == 1; };
8879                 break;
8880             case "date":
8881                 cv = function(v){
8882                     if(!v){
8883                         return '';
8884                     }
8885                     if(v instanceof Date){
8886                         return v;
8887                     }
8888                     if(dateFormat){
8889                         if(dateFormat == "timestamp"){
8890                             return new Date(v*1000);
8891                         }
8892                         return Date.parseDate(v, dateFormat);
8893                     }
8894                     var parsed = Date.parse(v);
8895                     return parsed ? new Date(parsed) : null;
8896                 };
8897              break;
8898             
8899         }
8900         this.convert = cv;
8901     }
8902 };
8903
8904 Roo.data.Field.prototype = {
8905     dateFormat: null,
8906     defaultValue: "",
8907     mapping: null,
8908     sortType : null,
8909     sortDir : "ASC"
8910 };/*
8911  * Based on:
8912  * Ext JS Library 1.1.1
8913  * Copyright(c) 2006-2007, Ext JS, LLC.
8914  *
8915  * Originally Released Under LGPL - original licence link has changed is not relivant.
8916  *
8917  * Fork - LGPL
8918  * <script type="text/javascript">
8919  */
8920  
8921 // Base class for reading structured data from a data source.  This class is intended to be
8922 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
8923
8924 /**
8925  * @class Roo.data.DataReader
8926  * Base class for reading structured data from a data source.  This class is intended to be
8927  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
8928  */
8929
8930 Roo.data.DataReader = function(meta, recordType){
8931     
8932     this.meta = meta;
8933     
8934     this.recordType = recordType instanceof Array ? 
8935         Roo.data.Record.create(recordType) : recordType;
8936 };
8937
8938 Roo.data.DataReader.prototype = {
8939      /**
8940      * Create an empty record
8941      * @param {Object} data (optional) - overlay some values
8942      * @return {Roo.data.Record} record created.
8943      */
8944     newRow :  function(d) {
8945         var da =  {};
8946         this.recordType.prototype.fields.each(function(c) {
8947             switch( c.type) {
8948                 case 'int' : da[c.name] = 0; break;
8949                 case 'date' : da[c.name] = new Date(); break;
8950                 case 'float' : da[c.name] = 0.0; break;
8951                 case 'boolean' : da[c.name] = false; break;
8952                 default : da[c.name] = ""; break;
8953             }
8954             
8955         });
8956         return new this.recordType(Roo.apply(da, d));
8957     }
8958     
8959 };/*
8960  * Based on:
8961  * Ext JS Library 1.1.1
8962  * Copyright(c) 2006-2007, Ext JS, LLC.
8963  *
8964  * Originally Released Under LGPL - original licence link has changed is not relivant.
8965  *
8966  * Fork - LGPL
8967  * <script type="text/javascript">
8968  */
8969
8970 /**
8971  * @class Roo.data.DataProxy
8972  * @extends Roo.data.Observable
8973  * This class is an abstract base class for implementations which provide retrieval of
8974  * unformatted data objects.<br>
8975  * <p>
8976  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
8977  * (of the appropriate type which knows how to parse the data object) to provide a block of
8978  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
8979  * <p>
8980  * Custom implementations must implement the load method as described in
8981  * {@link Roo.data.HttpProxy#load}.
8982  */
8983 Roo.data.DataProxy = function(){
8984     this.addEvents({
8985         /**
8986          * @event beforeload
8987          * Fires before a network request is made to retrieve a data object.
8988          * @param {Object} This DataProxy object.
8989          * @param {Object} params The params parameter to the load function.
8990          */
8991         beforeload : true,
8992         /**
8993          * @event load
8994          * Fires before the load method's callback is called.
8995          * @param {Object} This DataProxy object.
8996          * @param {Object} o The data object.
8997          * @param {Object} arg The callback argument object passed to the load function.
8998          */
8999         load : true,
9000         /**
9001          * @event loadexception
9002          * Fires if an Exception occurs during data retrieval.
9003          * @param {Object} This DataProxy object.
9004          * @param {Object} o The data object.
9005          * @param {Object} arg The callback argument object passed to the load function.
9006          * @param {Object} e The Exception.
9007          */
9008         loadexception : true
9009     });
9010     Roo.data.DataProxy.superclass.constructor.call(this);
9011 };
9012
9013 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9014
9015     /**
9016      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9017      */
9018 /*
9019  * Based on:
9020  * Ext JS Library 1.1.1
9021  * Copyright(c) 2006-2007, Ext JS, LLC.
9022  *
9023  * Originally Released Under LGPL - original licence link has changed is not relivant.
9024  *
9025  * Fork - LGPL
9026  * <script type="text/javascript">
9027  */
9028 /**
9029  * @class Roo.data.MemoryProxy
9030  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9031  * to the Reader when its load method is called.
9032  * @constructor
9033  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9034  */
9035 Roo.data.MemoryProxy = function(data){
9036     if (data.data) {
9037         data = data.data;
9038     }
9039     Roo.data.MemoryProxy.superclass.constructor.call(this);
9040     this.data = data;
9041 };
9042
9043 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9044     /**
9045      * Load data from the requested source (in this case an in-memory
9046      * data object passed to the constructor), read the data object into
9047      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9048      * process that block using the passed callback.
9049      * @param {Object} params This parameter is not used by the MemoryProxy class.
9050      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9051      * object into a block of Roo.data.Records.
9052      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9053      * The function must be passed <ul>
9054      * <li>The Record block object</li>
9055      * <li>The "arg" argument from the load function</li>
9056      * <li>A boolean success indicator</li>
9057      * </ul>
9058      * @param {Object} scope The scope in which to call the callback
9059      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9060      */
9061     load : function(params, reader, callback, scope, arg){
9062         params = params || {};
9063         var result;
9064         try {
9065             result = reader.readRecords(this.data);
9066         }catch(e){
9067             this.fireEvent("loadexception", this, arg, null, e);
9068             callback.call(scope, null, arg, false);
9069             return;
9070         }
9071         callback.call(scope, result, arg, true);
9072     },
9073     
9074     // private
9075     update : function(params, records){
9076         
9077     }
9078 });/*
9079  * Based on:
9080  * Ext JS Library 1.1.1
9081  * Copyright(c) 2006-2007, Ext JS, LLC.
9082  *
9083  * Originally Released Under LGPL - original licence link has changed is not relivant.
9084  *
9085  * Fork - LGPL
9086  * <script type="text/javascript">
9087  */
9088 /**
9089  * @class Roo.data.HttpProxy
9090  * @extends Roo.data.DataProxy
9091  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9092  * configured to reference a certain URL.<br><br>
9093  * <p>
9094  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9095  * from which the running page was served.<br><br>
9096  * <p>
9097  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9098  * <p>
9099  * Be aware that to enable the browser to parse an XML document, the server must set
9100  * the Content-Type header in the HTTP response to "text/xml".
9101  * @constructor
9102  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9103  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9104  * will be used to make the request.
9105  */
9106 Roo.data.HttpProxy = function(conn){
9107     Roo.data.HttpProxy.superclass.constructor.call(this);
9108     // is conn a conn config or a real conn?
9109     this.conn = conn;
9110     this.useAjax = !conn || !conn.events;
9111   
9112 };
9113
9114 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9115     // thse are take from connection...
9116     
9117     /**
9118      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9119      */
9120     /**
9121      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9122      * extra parameters to each request made by this object. (defaults to undefined)
9123      */
9124     /**
9125      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9126      *  to each request made by this object. (defaults to undefined)
9127      */
9128     /**
9129      * @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)
9130      */
9131     /**
9132      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9133      */
9134      /**
9135      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9136      * @type Boolean
9137      */
9138   
9139
9140     /**
9141      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9142      * @type Boolean
9143      */
9144     /**
9145      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9146      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9147      * a finer-grained basis than the DataProxy events.
9148      */
9149     getConnection : function(){
9150         return this.useAjax ? Roo.Ajax : this.conn;
9151     },
9152
9153     /**
9154      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9155      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9156      * process that block using the passed callback.
9157      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9158      * for the request to the remote server.
9159      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9160      * object into a block of Roo.data.Records.
9161      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9162      * The function must be passed <ul>
9163      * <li>The Record block object</li>
9164      * <li>The "arg" argument from the load function</li>
9165      * <li>A boolean success indicator</li>
9166      * </ul>
9167      * @param {Object} scope The scope in which to call the callback
9168      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9169      */
9170     load : function(params, reader, callback, scope, arg){
9171         if(this.fireEvent("beforeload", this, params) !== false){
9172             var  o = {
9173                 params : params || {},
9174                 request: {
9175                     callback : callback,
9176                     scope : scope,
9177                     arg : arg
9178                 },
9179                 reader: reader,
9180                 callback : this.loadResponse,
9181                 scope: this
9182             };
9183             if(this.useAjax){
9184                 Roo.applyIf(o, this.conn);
9185                 if(this.activeRequest){
9186                     Roo.Ajax.abort(this.activeRequest);
9187                 }
9188                 this.activeRequest = Roo.Ajax.request(o);
9189             }else{
9190                 this.conn.request(o);
9191             }
9192         }else{
9193             callback.call(scope||this, null, arg, false);
9194         }
9195     },
9196
9197     // private
9198     loadResponse : function(o, success, response){
9199         delete this.activeRequest;
9200         if(!success){
9201             this.fireEvent("loadexception", this, o, response);
9202             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9203             return;
9204         }
9205         var result;
9206         try {
9207             result = o.reader.read(response);
9208         }catch(e){
9209             this.fireEvent("loadexception", this, o, response, e);
9210             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9211             return;
9212         }
9213         
9214         this.fireEvent("load", this, o, o.request.arg);
9215         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9216     },
9217
9218     // private
9219     update : function(dataSet){
9220
9221     },
9222
9223     // private
9224     updateResponse : function(dataSet){
9225
9226     }
9227 });/*
9228  * Based on:
9229  * Ext JS Library 1.1.1
9230  * Copyright(c) 2006-2007, Ext JS, LLC.
9231  *
9232  * Originally Released Under LGPL - original licence link has changed is not relivant.
9233  *
9234  * Fork - LGPL
9235  * <script type="text/javascript">
9236  */
9237
9238 /**
9239  * @class Roo.data.ScriptTagProxy
9240  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9241  * other than the originating domain of the running page.<br><br>
9242  * <p>
9243  * <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
9244  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9245  * <p>
9246  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9247  * source code that is used as the source inside a &lt;script> tag.<br><br>
9248  * <p>
9249  * In order for the browser to process the returned data, the server must wrap the data object
9250  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9251  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9252  * depending on whether the callback name was passed:
9253  * <p>
9254  * <pre><code>
9255 boolean scriptTag = false;
9256 String cb = request.getParameter("callback");
9257 if (cb != null) {
9258     scriptTag = true;
9259     response.setContentType("text/javascript");
9260 } else {
9261     response.setContentType("application/x-json");
9262 }
9263 Writer out = response.getWriter();
9264 if (scriptTag) {
9265     out.write(cb + "(");
9266 }
9267 out.print(dataBlock.toJsonString());
9268 if (scriptTag) {
9269     out.write(");");
9270 }
9271 </pre></code>
9272  *
9273  * @constructor
9274  * @param {Object} config A configuration object.
9275  */
9276 Roo.data.ScriptTagProxy = function(config){
9277     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9278     Roo.apply(this, config);
9279     this.head = document.getElementsByTagName("head")[0];
9280 };
9281
9282 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9283
9284 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9285     /**
9286      * @cfg {String} url The URL from which to request the data object.
9287      */
9288     /**
9289      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9290      */
9291     timeout : 30000,
9292     /**
9293      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9294      * the server the name of the callback function set up by the load call to process the returned data object.
9295      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9296      * javascript output which calls this named function passing the data object as its only parameter.
9297      */
9298     callbackParam : "callback",
9299     /**
9300      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9301      * name to the request.
9302      */
9303     nocache : true,
9304
9305     /**
9306      * Load data from the configured URL, read the data object into
9307      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9308      * process that block using the passed callback.
9309      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9310      * for the request to the remote server.
9311      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9312      * object into a block of Roo.data.Records.
9313      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9314      * The function must be passed <ul>
9315      * <li>The Record block object</li>
9316      * <li>The "arg" argument from the load function</li>
9317      * <li>A boolean success indicator</li>
9318      * </ul>
9319      * @param {Object} scope The scope in which to call the callback
9320      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9321      */
9322     load : function(params, reader, callback, scope, arg){
9323         if(this.fireEvent("beforeload", this, params) !== false){
9324
9325             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9326
9327             var url = this.url;
9328             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9329             if(this.nocache){
9330                 url += "&_dc=" + (new Date().getTime());
9331             }
9332             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9333             var trans = {
9334                 id : transId,
9335                 cb : "stcCallback"+transId,
9336                 scriptId : "stcScript"+transId,
9337                 params : params,
9338                 arg : arg,
9339                 url : url,
9340                 callback : callback,
9341                 scope : scope,
9342                 reader : reader
9343             };
9344             var conn = this;
9345
9346             window[trans.cb] = function(o){
9347                 conn.handleResponse(o, trans);
9348             };
9349
9350             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9351
9352             if(this.autoAbort !== false){
9353                 this.abort();
9354             }
9355
9356             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9357
9358             var script = document.createElement("script");
9359             script.setAttribute("src", url);
9360             script.setAttribute("type", "text/javascript");
9361             script.setAttribute("id", trans.scriptId);
9362             this.head.appendChild(script);
9363
9364             this.trans = trans;
9365         }else{
9366             callback.call(scope||this, null, arg, false);
9367         }
9368     },
9369
9370     // private
9371     isLoading : function(){
9372         return this.trans ? true : false;
9373     },
9374
9375     /**
9376      * Abort the current server request.
9377      */
9378     abort : function(){
9379         if(this.isLoading()){
9380             this.destroyTrans(this.trans);
9381         }
9382     },
9383
9384     // private
9385     destroyTrans : function(trans, isLoaded){
9386         this.head.removeChild(document.getElementById(trans.scriptId));
9387         clearTimeout(trans.timeoutId);
9388         if(isLoaded){
9389             window[trans.cb] = undefined;
9390             try{
9391                 delete window[trans.cb];
9392             }catch(e){}
9393         }else{
9394             // if hasn't been loaded, wait for load to remove it to prevent script error
9395             window[trans.cb] = function(){
9396                 window[trans.cb] = undefined;
9397                 try{
9398                     delete window[trans.cb];
9399                 }catch(e){}
9400             };
9401         }
9402     },
9403
9404     // private
9405     handleResponse : function(o, trans){
9406         this.trans = false;
9407         this.destroyTrans(trans, true);
9408         var result;
9409         try {
9410             result = trans.reader.readRecords(o);
9411         }catch(e){
9412             this.fireEvent("loadexception", this, o, trans.arg, e);
9413             trans.callback.call(trans.scope||window, null, trans.arg, false);
9414             return;
9415         }
9416         this.fireEvent("load", this, o, trans.arg);
9417         trans.callback.call(trans.scope||window, result, trans.arg, true);
9418     },
9419
9420     // private
9421     handleFailure : function(trans){
9422         this.trans = false;
9423         this.destroyTrans(trans, false);
9424         this.fireEvent("loadexception", this, null, trans.arg);
9425         trans.callback.call(trans.scope||window, null, trans.arg, false);
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.JsonReader
9440  * @extends Roo.data.DataReader
9441  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9442  * based on mappings in a provided Roo.data.Record constructor.
9443  * 
9444  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9445  * in the reply previously. 
9446  * 
9447  * <p>
9448  * Example code:
9449  * <pre><code>
9450 var RecordDef = Roo.data.Record.create([
9451     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9452     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9453 ]);
9454 var myReader = new Roo.data.JsonReader({
9455     totalProperty: "results",    // The property which contains the total dataset size (optional)
9456     root: "rows",                // The property which contains an Array of row objects
9457     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9458 }, RecordDef);
9459 </code></pre>
9460  * <p>
9461  * This would consume a JSON file like this:
9462  * <pre><code>
9463 { 'results': 2, 'rows': [
9464     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9465     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9466 }
9467 </code></pre>
9468  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9469  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9470  * paged from the remote server.
9471  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9472  * @cfg {String} root name of the property which contains the Array of row objects.
9473  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9474  * @constructor
9475  * Create a new JsonReader
9476  * @param {Object} meta Metadata configuration options
9477  * @param {Object} recordType Either an Array of field definition objects,
9478  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9479  */
9480 Roo.data.JsonReader = function(meta, recordType){
9481     
9482     meta = meta || {};
9483     // set some defaults:
9484     Roo.applyIf(meta, {
9485         totalProperty: 'total',
9486         successProperty : 'success',
9487         root : 'data',
9488         id : 'id'
9489     });
9490     
9491     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9492 };
9493 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9494     
9495     /**
9496      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9497      * Used by Store query builder to append _requestMeta to params.
9498      * 
9499      */
9500     metaFromRemote : false,
9501     /**
9502      * This method is only used by a DataProxy which has retrieved data from a remote server.
9503      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9504      * @return {Object} data A data block which is used by an Roo.data.Store object as
9505      * a cache of Roo.data.Records.
9506      */
9507     read : function(response){
9508         var json = response.responseText;
9509        
9510         var o = /* eval:var:o */ eval("("+json+")");
9511         if(!o) {
9512             throw {message: "JsonReader.read: Json object not found"};
9513         }
9514         
9515         if(o.metaData){
9516             
9517             delete this.ef;
9518             this.metaFromRemote = true;
9519             this.meta = o.metaData;
9520             this.recordType = Roo.data.Record.create(o.metaData.fields);
9521             this.onMetaChange(this.meta, this.recordType, o);
9522         }
9523         return this.readRecords(o);
9524     },
9525
9526     // private function a store will implement
9527     onMetaChange : function(meta, recordType, o){
9528
9529     },
9530
9531     /**
9532          * @ignore
9533          */
9534     simpleAccess: function(obj, subsc) {
9535         return obj[subsc];
9536     },
9537
9538         /**
9539          * @ignore
9540          */
9541     getJsonAccessor: function(){
9542         var re = /[\[\.]/;
9543         return function(expr) {
9544             try {
9545                 return(re.test(expr))
9546                     ? new Function("obj", "return obj." + expr)
9547                     : function(obj){
9548                         return obj[expr];
9549                     };
9550             } catch(e){}
9551             return Roo.emptyFn;
9552         };
9553     }(),
9554
9555     /**
9556      * Create a data block containing Roo.data.Records from an XML document.
9557      * @param {Object} o An object which contains an Array of row objects in the property specified
9558      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9559      * which contains the total size of the dataset.
9560      * @return {Object} data A data block which is used by an Roo.data.Store object as
9561      * a cache of Roo.data.Records.
9562      */
9563     readRecords : function(o){
9564         /**
9565          * After any data loads, the raw JSON data is available for further custom processing.
9566          * @type Object
9567          */
9568         this.o = o;
9569         var s = this.meta, Record = this.recordType,
9570             f = Record.prototype.fields, fi = f.items, fl = f.length;
9571
9572 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
9573         if (!this.ef) {
9574             if(s.totalProperty) {
9575                     this.getTotal = this.getJsonAccessor(s.totalProperty);
9576                 }
9577                 if(s.successProperty) {
9578                     this.getSuccess = this.getJsonAccessor(s.successProperty);
9579                 }
9580                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
9581                 if (s.id) {
9582                         var g = this.getJsonAccessor(s.id);
9583                         this.getId = function(rec) {
9584                                 var r = g(rec);
9585                                 return (r === undefined || r === "") ? null : r;
9586                         };
9587                 } else {
9588                         this.getId = function(){return null;};
9589                 }
9590             this.ef = [];
9591             for(var jj = 0; jj < fl; jj++){
9592                 f = fi[jj];
9593                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
9594                 this.ef[jj] = this.getJsonAccessor(map);
9595             }
9596         }
9597
9598         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
9599         if(s.totalProperty){
9600             var vt = parseInt(this.getTotal(o), 10);
9601             if(!isNaN(vt)){
9602                 totalRecords = vt;
9603             }
9604         }
9605         if(s.successProperty){
9606             var vs = this.getSuccess(o);
9607             if(vs === false || vs === 'false'){
9608                 success = false;
9609             }
9610         }
9611         var records = [];
9612             for(var i = 0; i < c; i++){
9613                     var n = root[i];
9614                 var values = {};
9615                 var id = this.getId(n);
9616                 for(var j = 0; j < fl; j++){
9617                     f = fi[j];
9618                 var v = this.ef[j](n);
9619                 if (!f.convert) {
9620                     Roo.log('missing convert for ' + f.name);
9621                     Roo.log(f);
9622                     continue;
9623                 }
9624                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9625                 }
9626                 var record = new Record(values, id);
9627                 record.json = n;
9628                 records[i] = record;
9629             }
9630             return {
9631             raw : o,
9632                 success : success,
9633                 records : records,
9634                 totalRecords : totalRecords
9635             };
9636     }
9637 });/*
9638  * Based on:
9639  * Ext JS Library 1.1.1
9640  * Copyright(c) 2006-2007, Ext JS, LLC.
9641  *
9642  * Originally Released Under LGPL - original licence link has changed is not relivant.
9643  *
9644  * Fork - LGPL
9645  * <script type="text/javascript">
9646  */
9647
9648 /**
9649  * @class Roo.data.ArrayReader
9650  * @extends Roo.data.DataReader
9651  * Data reader class to create an Array of Roo.data.Record objects from an Array.
9652  * Each element of that Array represents a row of data fields. The
9653  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
9654  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
9655  * <p>
9656  * Example code:.
9657  * <pre><code>
9658 var RecordDef = Roo.data.Record.create([
9659     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
9660     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
9661 ]);
9662 var myReader = new Roo.data.ArrayReader({
9663     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
9664 }, RecordDef);
9665 </code></pre>
9666  * <p>
9667  * This would consume an Array like this:
9668  * <pre><code>
9669 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
9670   </code></pre>
9671  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
9672  * @constructor
9673  * Create a new JsonReader
9674  * @param {Object} meta Metadata configuration options.
9675  * @param {Object} recordType Either an Array of field definition objects
9676  * as specified to {@link Roo.data.Record#create},
9677  * or an {@link Roo.data.Record} object
9678  * created using {@link Roo.data.Record#create}.
9679  */
9680 Roo.data.ArrayReader = function(meta, recordType){
9681     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
9682 };
9683
9684 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
9685     /**
9686      * Create a data block containing Roo.data.Records from an XML document.
9687      * @param {Object} o An Array of row objects which represents the dataset.
9688      * @return {Object} data A data block which is used by an Roo.data.Store object as
9689      * a cache of Roo.data.Records.
9690      */
9691     readRecords : function(o){
9692         var sid = this.meta ? this.meta.id : null;
9693         var recordType = this.recordType, fields = recordType.prototype.fields;
9694         var records = [];
9695         var root = o;
9696             for(var i = 0; i < root.length; i++){
9697                     var n = root[i];
9698                 var values = {};
9699                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
9700                 for(var j = 0, jlen = fields.length; j < jlen; j++){
9701                 var f = fields.items[j];
9702                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
9703                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
9704                 v = f.convert(v);
9705                 values[f.name] = v;
9706             }
9707                 var record = new recordType(values, id);
9708                 record.json = n;
9709                 records[records.length] = record;
9710             }
9711             return {
9712                 records : records,
9713                 totalRecords : records.length
9714             };
9715     }
9716 });/*
9717  * - LGPL
9718  * * 
9719  */
9720
9721 /**
9722  * @class Roo.bootstrap.ComboBox
9723  * @extends Roo.bootstrap.TriggerField
9724  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
9725  * @cfg {Boolean} append (true|false) default false
9726  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
9727  * @constructor
9728  * Create a new ComboBox.
9729  * @param {Object} config Configuration options
9730  */
9731 Roo.bootstrap.ComboBox = function(config){
9732     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
9733     this.addEvents({
9734         /**
9735          * @event expand
9736          * Fires when the dropdown list is expanded
9737              * @param {Roo.bootstrap.ComboBox} combo This combo box
9738              */
9739         'expand' : true,
9740         /**
9741          * @event collapse
9742          * Fires when the dropdown list is collapsed
9743              * @param {Roo.bootstrap.ComboBox} combo This combo box
9744              */
9745         'collapse' : true,
9746         /**
9747          * @event beforeselect
9748          * Fires before a list item is selected. Return false to cancel the selection.
9749              * @param {Roo.bootstrap.ComboBox} combo This combo box
9750              * @param {Roo.data.Record} record The data record returned from the underlying store
9751              * @param {Number} index The index of the selected item in the dropdown list
9752              */
9753         'beforeselect' : true,
9754         /**
9755          * @event select
9756          * Fires when a list item is selected
9757              * @param {Roo.bootstrap.ComboBox} combo This combo box
9758              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
9759              * @param {Number} index The index of the selected item in the dropdown list
9760              */
9761         'select' : true,
9762         /**
9763          * @event beforequery
9764          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
9765          * The event object passed has these properties:
9766              * @param {Roo.bootstrap.ComboBox} combo This combo box
9767              * @param {String} query The query
9768              * @param {Boolean} forceAll true to force "all" query
9769              * @param {Boolean} cancel true to cancel the query
9770              * @param {Object} e The query event object
9771              */
9772         'beforequery': true,
9773          /**
9774          * @event add
9775          * Fires when the 'add' icon is pressed (add a listener to enable add button)
9776              * @param {Roo.bootstrap.ComboBox} combo This combo box
9777              */
9778         'add' : true,
9779         /**
9780          * @event edit
9781          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9782              * @param {Roo.bootstrap.ComboBox} combo This combo box
9783              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9784              */
9785         'edit' : true,
9786         /**
9787          * @event remove
9788          * Fires when the remove value from the combobox array
9789              * @param {Roo.bootstrap.ComboBox} combo This combo box
9790              */
9791         'remove' : true
9792         
9793     });
9794     
9795     
9796     this.selectedIndex = -1;
9797     if(this.mode == 'local'){
9798         if(config.queryDelay === undefined){
9799             this.queryDelay = 10;
9800         }
9801         if(config.minChars === undefined){
9802             this.minChars = 0;
9803         }
9804     }
9805 };
9806
9807 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
9808      
9809     /**
9810      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
9811      * rendering into an Roo.Editor, defaults to false)
9812      */
9813     /**
9814      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
9815      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
9816      */
9817     /**
9818      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
9819      */
9820     /**
9821      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
9822      * the dropdown list (defaults to undefined, with no header element)
9823      */
9824
9825      /**
9826      * @cfg {String/Roo.Template} tpl The template to use to render the output
9827      */
9828      
9829      /**
9830      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
9831      */
9832     listWidth: undefined,
9833     /**
9834      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
9835      * mode = 'remote' or 'text' if mode = 'local')
9836      */
9837     displayField: undefined,
9838     /**
9839      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
9840      * mode = 'remote' or 'value' if mode = 'local'). 
9841      * Note: use of a valueField requires the user make a selection
9842      * in order for a value to be mapped.
9843      */
9844     valueField: undefined,
9845     
9846     
9847     /**
9848      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
9849      * field's data value (defaults to the underlying DOM element's name)
9850      */
9851     hiddenName: undefined,
9852     /**
9853      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
9854      */
9855     listClass: '',
9856     /**
9857      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
9858      */
9859     selectedClass: 'active',
9860     
9861     /**
9862      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9863      */
9864     shadow:'sides',
9865     /**
9866      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
9867      * anchor positions (defaults to 'tl-bl')
9868      */
9869     listAlign: 'tl-bl?',
9870     /**
9871      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
9872      */
9873     maxHeight: 300,
9874     /**
9875      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
9876      * query specified by the allQuery config option (defaults to 'query')
9877      */
9878     triggerAction: 'query',
9879     /**
9880      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
9881      * (defaults to 4, does not apply if editable = false)
9882      */
9883     minChars : 4,
9884     /**
9885      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
9886      * delay (typeAheadDelay) if it matches a known value (defaults to false)
9887      */
9888     typeAhead: false,
9889     /**
9890      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
9891      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
9892      */
9893     queryDelay: 500,
9894     /**
9895      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
9896      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
9897      */
9898     pageSize: 0,
9899     /**
9900      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
9901      * when editable = true (defaults to false)
9902      */
9903     selectOnFocus:false,
9904     /**
9905      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
9906      */
9907     queryParam: 'query',
9908     /**
9909      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
9910      * when mode = 'remote' (defaults to 'Loading...')
9911      */
9912     loadingText: 'Loading...',
9913     /**
9914      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
9915      */
9916     resizable: false,
9917     /**
9918      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
9919      */
9920     handleHeight : 8,
9921     /**
9922      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
9923      * traditional select (defaults to true)
9924      */
9925     editable: true,
9926     /**
9927      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
9928      */
9929     allQuery: '',
9930     /**
9931      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
9932      */
9933     mode: 'remote',
9934     /**
9935      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
9936      * listWidth has a higher value)
9937      */
9938     minListWidth : 70,
9939     /**
9940      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
9941      * allow the user to set arbitrary text into the field (defaults to false)
9942      */
9943     forceSelection:false,
9944     /**
9945      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
9946      * if typeAhead = true (defaults to 250)
9947      */
9948     typeAheadDelay : 250,
9949     /**
9950      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
9951      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
9952      */
9953     valueNotFoundText : undefined,
9954     /**
9955      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
9956      */
9957     blockFocus : false,
9958     
9959     /**
9960      * @cfg {Boolean} disableClear Disable showing of clear button.
9961      */
9962     disableClear : false,
9963     /**
9964      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
9965      */
9966     alwaysQuery : false,
9967     
9968     /**
9969      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
9970      */
9971     multiple : false,
9972     
9973     //private
9974     addicon : false,
9975     editicon: false,
9976     
9977     page: 0,
9978     hasQuery: false,
9979     append: false,
9980     loadNext: false,
9981     autoFocus : true,
9982     item: [],
9983     
9984     // element that contains real text value.. (when hidden is used..)
9985      
9986     // private
9987     initEvents: function(){
9988         
9989         if (!this.store) {
9990             throw "can not find store for combo";
9991         }
9992         this.store = Roo.factory(this.store, Roo.data);
9993         
9994         
9995         
9996         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
9997         
9998         
9999         if(this.hiddenName){
10000             
10001             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10002             
10003             this.hiddenField.dom.value =
10004                 this.hiddenValue !== undefined ? this.hiddenValue :
10005                 this.value !== undefined ? this.value : '';
10006
10007             // prevent input submission
10008             this.el.dom.removeAttribute('name');
10009             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10010              
10011              
10012         }
10013         //if(Roo.isGecko){
10014         //    this.el.dom.setAttribute('autocomplete', 'off');
10015         //}
10016
10017         var cls = 'x-combo-list';
10018         this.list = this.el.select('ul.dropdown-menu',true).first();
10019
10020         //this.list = new Roo.Layer({
10021         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10022         //});
10023         
10024         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
10025         this.list.setWidth(lw);
10026         
10027         this.list.on('mouseover', this.onViewOver, this);
10028         this.list.on('mousemove', this.onViewMove, this);
10029         
10030         this.list.on('scroll', this.onViewScroll, this);
10031         
10032         /*
10033         this.list.swallowEvent('mousewheel');
10034         this.assetHeight = 0;
10035
10036         if(this.title){
10037             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10038             this.assetHeight += this.header.getHeight();
10039         }
10040
10041         this.innerList = this.list.createChild({cls:cls+'-inner'});
10042         this.innerList.on('mouseover', this.onViewOver, this);
10043         this.innerList.on('mousemove', this.onViewMove, this);
10044         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10045         
10046         if(this.allowBlank && !this.pageSize && !this.disableClear){
10047             this.footer = this.list.createChild({cls:cls+'-ft'});
10048             this.pageTb = new Roo.Toolbar(this.footer);
10049            
10050         }
10051         if(this.pageSize){
10052             this.footer = this.list.createChild({cls:cls+'-ft'});
10053             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10054                     {pageSize: this.pageSize});
10055             
10056         }
10057         
10058         if (this.pageTb && this.allowBlank && !this.disableClear) {
10059             var _this = this;
10060             this.pageTb.add(new Roo.Toolbar.Fill(), {
10061                 cls: 'x-btn-icon x-btn-clear',
10062                 text: '&#160;',
10063                 handler: function()
10064                 {
10065                     _this.collapse();
10066                     _this.clearValue();
10067                     _this.onSelect(false, -1);
10068                 }
10069             });
10070         }
10071         if (this.footer) {
10072             this.assetHeight += this.footer.getHeight();
10073         }
10074         */
10075             
10076         if(!this.tpl){
10077             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10078         }
10079
10080         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10081             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10082         });
10083         //this.view.wrapEl.setDisplayed(false);
10084         this.view.on('click', this.onViewClick, this);
10085         
10086         
10087         
10088         this.store.on('beforeload', this.onBeforeLoad, this);
10089         this.store.on('load', this.onLoad, this);
10090         this.store.on('loadexception', this.onLoadException, this);
10091         /*
10092         if(this.resizable){
10093             this.resizer = new Roo.Resizable(this.list,  {
10094                pinned:true, handles:'se'
10095             });
10096             this.resizer.on('resize', function(r, w, h){
10097                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10098                 this.listWidth = w;
10099                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10100                 this.restrictHeight();
10101             }, this);
10102             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10103         }
10104         */
10105         if(!this.editable){
10106             this.editable = true;
10107             this.setEditable(false);
10108         }
10109         
10110         /*
10111         
10112         if (typeof(this.events.add.listeners) != 'undefined') {
10113             
10114             this.addicon = this.wrap.createChild(
10115                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10116        
10117             this.addicon.on('click', function(e) {
10118                 this.fireEvent('add', this);
10119             }, this);
10120         }
10121         if (typeof(this.events.edit.listeners) != 'undefined') {
10122             
10123             this.editicon = this.wrap.createChild(
10124                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10125             if (this.addicon) {
10126                 this.editicon.setStyle('margin-left', '40px');
10127             }
10128             this.editicon.on('click', function(e) {
10129                 
10130                 // we fire even  if inothing is selected..
10131                 this.fireEvent('edit', this, this.lastData );
10132                 
10133             }, this);
10134         }
10135         */
10136         
10137         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10138             "up" : function(e){
10139                 this.inKeyMode = true;
10140                 this.selectPrev();
10141             },
10142
10143             "down" : function(e){
10144                 if(!this.isExpanded()){
10145                     this.onTriggerClick();
10146                 }else{
10147                     this.inKeyMode = true;
10148                     this.selectNext();
10149                 }
10150             },
10151
10152             "enter" : function(e){
10153 //                this.onViewClick();
10154                 //return true;
10155                 this.collapse();
10156                 
10157                 if(this.fireEvent("specialkey", this, e)){
10158                     this.onViewClick(false);
10159                 }
10160                 
10161                 return true;
10162             },
10163
10164             "esc" : function(e){
10165                 this.collapse();
10166             },
10167
10168             "tab" : function(e){
10169                 this.collapse();
10170                 
10171                 if(this.fireEvent("specialkey", this, e)){
10172                     this.onViewClick(false);
10173                 }
10174                 
10175                 return true;
10176             },
10177
10178             scope : this,
10179
10180             doRelay : function(foo, bar, hname){
10181                 if(hname == 'down' || this.scope.isExpanded()){
10182                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10183                 }
10184                 return true;
10185             },
10186
10187             forceKeyDown: true
10188         });
10189         
10190         
10191         this.queryDelay = Math.max(this.queryDelay || 10,
10192                 this.mode == 'local' ? 10 : 250);
10193         
10194         
10195         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10196         
10197         if(this.typeAhead){
10198             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10199         }
10200         if(this.editable !== false){
10201             this.inputEl().on("keyup", this.onKeyUp, this);
10202         }
10203         if(this.forceSelection){
10204             this.inputEl().on('blur', this.doForce, this);
10205         }
10206         
10207         if(this.multiple){
10208             this.choices = this.el.select('ul.select2-choices', true).first();
10209             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10210         }
10211     },
10212
10213     onDestroy : function(){
10214         if(this.view){
10215             this.view.setStore(null);
10216             this.view.el.removeAllListeners();
10217             this.view.el.remove();
10218             this.view.purgeListeners();
10219         }
10220         if(this.list){
10221             this.list.dom.innerHTML  = '';
10222         }
10223         if(this.store){
10224             this.store.un('beforeload', this.onBeforeLoad, this);
10225             this.store.un('load', this.onLoad, this);
10226             this.store.un('loadexception', this.onLoadException, this);
10227         }
10228         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10229     },
10230
10231     // private
10232     fireKey : function(e){
10233         if(e.isNavKeyPress() && !this.list.isVisible()){
10234             this.fireEvent("specialkey", this, e);
10235         }
10236     },
10237
10238     // private
10239     onResize: function(w, h){
10240 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10241 //        
10242 //        if(typeof w != 'number'){
10243 //            // we do not handle it!?!?
10244 //            return;
10245 //        }
10246 //        var tw = this.trigger.getWidth();
10247 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10248 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10249 //        var x = w - tw;
10250 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10251 //            
10252 //        //this.trigger.setStyle('left', x+'px');
10253 //        
10254 //        if(this.list && this.listWidth === undefined){
10255 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10256 //            this.list.setWidth(lw);
10257 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10258 //        }
10259         
10260     
10261         
10262     },
10263
10264     /**
10265      * Allow or prevent the user from directly editing the field text.  If false is passed,
10266      * the user will only be able to select from the items defined in the dropdown list.  This method
10267      * is the runtime equivalent of setting the 'editable' config option at config time.
10268      * @param {Boolean} value True to allow the user to directly edit the field text
10269      */
10270     setEditable : function(value){
10271         if(value == this.editable){
10272             return;
10273         }
10274         this.editable = value;
10275         if(!value){
10276             this.inputEl().dom.setAttribute('readOnly', true);
10277             this.inputEl().on('mousedown', this.onTriggerClick,  this);
10278             this.inputEl().addClass('x-combo-noedit');
10279         }else{
10280             this.inputEl().dom.setAttribute('readOnly', false);
10281             this.inputEl().un('mousedown', this.onTriggerClick,  this);
10282             this.inputEl().removeClass('x-combo-noedit');
10283         }
10284     },
10285
10286     // private
10287     
10288     onBeforeLoad : function(combo,opts){
10289         if(!this.hasFocus){
10290             return;
10291         }
10292          if (!opts.add) {
10293             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
10294          }
10295         this.restrictHeight();
10296         this.selectedIndex = -1;
10297     },
10298
10299     // private
10300     onLoad : function(){
10301         
10302         this.hasQuery = false;
10303         
10304         if(!this.hasFocus){
10305             return;
10306         }
10307         
10308         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10309             this.loading.hide();
10310         }
10311         
10312         if(this.store.getCount() > 0){
10313             this.expand();
10314             this.restrictHeight();
10315             if(this.lastQuery == this.allQuery){
10316                 if(this.editable){
10317                     this.inputEl().dom.select();
10318                 }
10319                 if(!this.selectByValue(this.value, true) && this.autoFocus){
10320                     this.select(0, true);
10321                 }
10322             }else{
10323                 if(this.autoFocus){
10324                     this.selectNext();
10325                 }
10326                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
10327                     this.taTask.delay(this.typeAheadDelay);
10328                 }
10329             }
10330         }else{
10331             this.onEmptyResults();
10332         }
10333         
10334         //this.el.focus();
10335     },
10336     // private
10337     onLoadException : function()
10338     {
10339         this.hasQuery = false;
10340         
10341         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10342             this.loading.hide();
10343         }
10344         
10345         this.collapse();
10346         Roo.log(this.store.reader.jsonData);
10347         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
10348             // fixme
10349             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
10350         }
10351         
10352         
10353     },
10354     // private
10355     onTypeAhead : function(){
10356         if(this.store.getCount() > 0){
10357             var r = this.store.getAt(0);
10358             var newValue = r.data[this.displayField];
10359             var len = newValue.length;
10360             var selStart = this.getRawValue().length;
10361             
10362             if(selStart != len){
10363                 this.setRawValue(newValue);
10364                 this.selectText(selStart, newValue.length);
10365             }
10366         }
10367     },
10368
10369     // private
10370     onSelect : function(record, index){
10371         
10372         if(this.fireEvent('beforeselect', this, record, index) !== false){
10373         
10374             this.setFromData(index > -1 ? record.data : false);
10375             
10376             this.collapse();
10377             this.fireEvent('select', this, record, index);
10378         }
10379     },
10380
10381     /**
10382      * Returns the currently selected field value or empty string if no value is set.
10383      * @return {String} value The selected value
10384      */
10385     getValue : function(){
10386         
10387         if(this.multiple){
10388             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
10389         }
10390         
10391         if(this.valueField){
10392             return typeof this.value != 'undefined' ? this.value : '';
10393         }else{
10394             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
10395         }
10396     },
10397
10398     /**
10399      * Clears any text/value currently set in the field
10400      */
10401     clearValue : function(){
10402         if(this.hiddenField){
10403             this.hiddenField.dom.value = '';
10404         }
10405         this.value = '';
10406         this.setRawValue('');
10407         this.lastSelectionText = '';
10408         
10409     },
10410
10411     /**
10412      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
10413      * will be displayed in the field.  If the value does not match the data value of an existing item,
10414      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
10415      * Otherwise the field will be blank (although the value will still be set).
10416      * @param {String} value The value to match
10417      */
10418     setValue : function(v){
10419         if(this.multiple){
10420             this.syncValue();
10421             return;
10422         }
10423         
10424         var text = v;
10425         if(this.valueField){
10426             var r = this.findRecord(this.valueField, v);
10427             if(r){
10428                 text = r.data[this.displayField];
10429             }else if(this.valueNotFoundText !== undefined){
10430                 text = this.valueNotFoundText;
10431             }
10432         }
10433         this.lastSelectionText = text;
10434         if(this.hiddenField){
10435             this.hiddenField.dom.value = v;
10436         }
10437         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
10438         this.value = v;
10439     },
10440     /**
10441      * @property {Object} the last set data for the element
10442      */
10443     
10444     lastData : false,
10445     /**
10446      * Sets the value of the field based on a object which is related to the record format for the store.
10447      * @param {Object} value the value to set as. or false on reset?
10448      */
10449     setFromData : function(o){
10450         
10451         if(this.multiple){
10452             this.addItem(o);
10453             return;
10454         }
10455             
10456         var dv = ''; // display value
10457         var vv = ''; // value value..
10458         this.lastData = o;
10459         if (this.displayField) {
10460             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10461         } else {
10462             // this is an error condition!!!
10463             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10464         }
10465         
10466         if(this.valueField){
10467             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
10468         }
10469         
10470         if(this.hiddenField){
10471             this.hiddenField.dom.value = vv;
10472             
10473             this.lastSelectionText = dv;
10474             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
10475             this.value = vv;
10476             return;
10477         }
10478         // no hidden field.. - we store the value in 'value', but still display
10479         // display field!!!!
10480         this.lastSelectionText = dv;
10481         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
10482         this.value = vv;
10483         
10484         
10485     },
10486     // private
10487     reset : function(){
10488         // overridden so that last data is reset..
10489         this.setValue(this.originalValue);
10490         this.clearInvalid();
10491         this.lastData = false;
10492         if (this.view) {
10493             this.view.clearSelections();
10494         }
10495     },
10496     // private
10497     findRecord : function(prop, value){
10498         var record;
10499         if(this.store.getCount() > 0){
10500             this.store.each(function(r){
10501                 if(r.data[prop] == value){
10502                     record = r;
10503                     return false;
10504                 }
10505                 return true;
10506             });
10507         }
10508         return record;
10509     },
10510     
10511     getName: function()
10512     {
10513         // returns hidden if it's set..
10514         if (!this.rendered) {return ''};
10515         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
10516         
10517     },
10518     // private
10519     onViewMove : function(e, t){
10520         this.inKeyMode = false;
10521     },
10522
10523     // private
10524     onViewOver : function(e, t){
10525         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
10526             return;
10527         }
10528         var item = this.view.findItemFromChild(t);
10529         if(item){
10530             var index = this.view.indexOf(item);
10531             this.select(index, false);
10532         }
10533     },
10534
10535     // private
10536     onViewClick : function(doFocus)
10537     {
10538         var index = this.view.getSelectedIndexes()[0];
10539         var r = this.store.getAt(index);
10540         if(r){
10541             this.onSelect(r, index);
10542         }
10543         if(doFocus !== false && !this.blockFocus){
10544             this.inputEl().focus();
10545         }
10546     },
10547
10548     // private
10549     restrictHeight : function(){
10550         //this.innerList.dom.style.height = '';
10551         //var inner = this.innerList.dom;
10552         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
10553         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
10554         //this.list.beginUpdate();
10555         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
10556         this.list.alignTo(this.inputEl(), this.listAlign);
10557         //this.list.endUpdate();
10558     },
10559
10560     // private
10561     onEmptyResults : function(){
10562         this.collapse();
10563     },
10564
10565     /**
10566      * Returns true if the dropdown list is expanded, else false.
10567      */
10568     isExpanded : function(){
10569         return this.list.isVisible();
10570     },
10571
10572     /**
10573      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
10574      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
10575      * @param {String} value The data value of the item to select
10576      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
10577      * selected item if it is not currently in view (defaults to true)
10578      * @return {Boolean} True if the value matched an item in the list, else false
10579      */
10580     selectByValue : function(v, scrollIntoView){
10581         if(v !== undefined && v !== null){
10582             var r = this.findRecord(this.valueField || this.displayField, v);
10583             if(r){
10584                 this.select(this.store.indexOf(r), scrollIntoView);
10585                 return true;
10586             }
10587         }
10588         return false;
10589     },
10590
10591     /**
10592      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
10593      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
10594      * @param {Number} index The zero-based index of the list item to select
10595      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
10596      * selected item if it is not currently in view (defaults to true)
10597      */
10598     select : function(index, scrollIntoView){
10599         this.selectedIndex = index;
10600         this.view.select(index);
10601         if(scrollIntoView !== false){
10602             var el = this.view.getNode(index);
10603             if(el){
10604                 //this.innerList.scrollChildIntoView(el, false);
10605                 
10606             }
10607         }
10608     },
10609
10610     // private
10611     selectNext : function(){
10612         var ct = this.store.getCount();
10613         if(ct > 0){
10614             if(this.selectedIndex == -1){
10615                 this.select(0);
10616             }else if(this.selectedIndex < ct-1){
10617                 this.select(this.selectedIndex+1);
10618             }
10619         }
10620     },
10621
10622     // private
10623     selectPrev : function(){
10624         var ct = this.store.getCount();
10625         if(ct > 0){
10626             if(this.selectedIndex == -1){
10627                 this.select(0);
10628             }else if(this.selectedIndex != 0){
10629                 this.select(this.selectedIndex-1);
10630             }
10631         }
10632     },
10633
10634     // private
10635     onKeyUp : function(e){
10636         if(this.editable !== false && !e.isSpecialKey()){
10637             this.lastKey = e.getKey();
10638             this.dqTask.delay(this.queryDelay);
10639         }
10640     },
10641
10642     // private
10643     validateBlur : function(){
10644         return !this.list || !this.list.isVisible();   
10645     },
10646
10647     // private
10648     initQuery : function(){
10649         this.doQuery(this.getRawValue());
10650     },
10651
10652     // private
10653     doForce : function(){
10654         if(this.inputEl().dom.value.length > 0){
10655             this.inputEl().dom.value =
10656                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
10657              
10658         }
10659     },
10660
10661     /**
10662      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
10663      * query allowing the query action to be canceled if needed.
10664      * @param {String} query The SQL query to execute
10665      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
10666      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
10667      * saved in the current store (defaults to false)
10668      */
10669     doQuery : function(q, forceAll){
10670         
10671         if(q === undefined || q === null){
10672             q = '';
10673         }
10674         var qe = {
10675             query: q,
10676             forceAll: forceAll,
10677             combo: this,
10678             cancel:false
10679         };
10680         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
10681             return false;
10682         }
10683         q = qe.query;
10684         
10685         forceAll = qe.forceAll;
10686         if(forceAll === true || (q.length >= this.minChars)){
10687             
10688             this.hasQuery = true;
10689             
10690             if(this.lastQuery != q || this.alwaysQuery){
10691                 this.lastQuery = q;
10692                 if(this.mode == 'local'){
10693                     this.selectedIndex = -1;
10694                     if(forceAll){
10695                         this.store.clearFilter();
10696                     }else{
10697                         this.store.filter(this.displayField, q);
10698                     }
10699                     this.onLoad();
10700                 }else{
10701                     this.store.baseParams[this.queryParam] = q;
10702                     
10703                     var options = {params : this.getParams(q)};
10704                     
10705                     if(this.loadNext){
10706                         options.add = true;
10707                         options.params.start = this.page * this.pageSize;
10708                     }
10709                     
10710                     this.store.load(options);
10711                     /*
10712                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
10713                      *  we should expand the list on onLoad
10714                      *  so command out it
10715                      */
10716 //                    this.expand();
10717                 }
10718             }else{
10719                 this.selectedIndex = -1;
10720                 this.onLoad();   
10721             }
10722         }
10723         
10724         this.loadNext = false;
10725     },
10726
10727     // private
10728     getParams : function(q){
10729         var p = {};
10730         //p[this.queryParam] = q;
10731         
10732         if(this.pageSize){
10733             p.start = 0;
10734             p.limit = this.pageSize;
10735         }
10736         return p;
10737     },
10738
10739     /**
10740      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
10741      */
10742     collapse : function(){
10743         if(!this.isExpanded()){
10744             return;
10745         }
10746         
10747         this.list.hide();
10748         Roo.get(document).un('mousedown', this.collapseIf, this);
10749         Roo.get(document).un('mousewheel', this.collapseIf, this);
10750         if (!this.editable) {
10751             Roo.get(document).un('keydown', this.listKeyPress, this);
10752         }
10753         this.fireEvent('collapse', this);
10754     },
10755
10756     // private
10757     collapseIf : function(e){
10758         var in_combo  = e.within(this.el);
10759         var in_list =  e.within(this.list);
10760         
10761         if (in_combo || in_list) {
10762             //e.stopPropagation();
10763             return;
10764         }
10765
10766         this.collapse();
10767         
10768     },
10769
10770     /**
10771      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
10772      */
10773     expand : function(){
10774        
10775         if(this.isExpanded() || !this.hasFocus){
10776             return;
10777         }
10778          Roo.log('expand');
10779         this.list.alignTo(this.inputEl(), this.listAlign);
10780         this.list.show();
10781         Roo.get(document).on('mousedown', this.collapseIf, this);
10782         Roo.get(document).on('mousewheel', this.collapseIf, this);
10783         if (!this.editable) {
10784             Roo.get(document).on('keydown', this.listKeyPress, this);
10785         }
10786         
10787         this.fireEvent('expand', this);
10788     },
10789
10790     // private
10791     // Implements the default empty TriggerField.onTriggerClick function
10792     onTriggerClick : function()
10793     {
10794         Roo.log('trigger click');
10795         
10796         if(this.disabled){
10797             return;
10798         }
10799         
10800         this.page = 0;
10801         this.loadNext = false;
10802         
10803         if(this.isExpanded()){
10804             this.collapse();
10805             if (!this.blockFocus) {
10806                 this.inputEl().focus();
10807             }
10808             
10809         }else {
10810             this.hasFocus = true;
10811             if(this.triggerAction == 'all') {
10812                 this.doQuery(this.allQuery, true);
10813             } else {
10814                 this.doQuery(this.getRawValue());
10815             }
10816             if (!this.blockFocus) {
10817                 this.inputEl().focus();
10818             }
10819         }
10820     },
10821     listKeyPress : function(e)
10822     {
10823         //Roo.log('listkeypress');
10824         // scroll to first matching element based on key pres..
10825         if (e.isSpecialKey()) {
10826             return false;
10827         }
10828         var k = String.fromCharCode(e.getKey()).toUpperCase();
10829         //Roo.log(k);
10830         var match  = false;
10831         var csel = this.view.getSelectedNodes();
10832         var cselitem = false;
10833         if (csel.length) {
10834             var ix = this.view.indexOf(csel[0]);
10835             cselitem  = this.store.getAt(ix);
10836             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
10837                 cselitem = false;
10838             }
10839             
10840         }
10841         
10842         this.store.each(function(v) { 
10843             if (cselitem) {
10844                 // start at existing selection.
10845                 if (cselitem.id == v.id) {
10846                     cselitem = false;
10847                 }
10848                 return true;
10849             }
10850                 
10851             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
10852                 match = this.store.indexOf(v);
10853                 return false;
10854             }
10855             return true;
10856         }, this);
10857         
10858         if (match === false) {
10859             return true; // no more action?
10860         }
10861         // scroll to?
10862         this.view.select(match);
10863         var sn = Roo.get(this.view.getSelectedNodes()[0])
10864         //sn.scrollIntoView(sn.dom.parentNode, false);
10865     },
10866     
10867     onViewScroll : function(e, t){
10868         
10869         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
10870             return;
10871         }
10872         
10873         this.hasQuery = true;
10874         
10875         this.loading = this.list.select('.loading', true).first();
10876         
10877         if(this.loading === null){
10878             this.list.createChild({
10879                 tag: 'div',
10880                 cls: 'loading select2-more-results select2-active',
10881                 html: 'Loading more results...'
10882             })
10883             
10884             this.loading = this.list.select('.loading', true).first();
10885             
10886             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
10887             
10888             this.loading.hide();
10889         }
10890         
10891         this.loading.show();
10892         
10893         var _combo = this;
10894         
10895         this.page++;
10896         this.loadNext = true;
10897         
10898         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
10899         
10900         return;
10901     },
10902     
10903     addItem : function(o)
10904     {   
10905         var dv = ''; // display value
10906         
10907         if (this.displayField) {
10908             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10909         } else {
10910             // this is an error condition!!!
10911             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10912         }
10913         
10914         if(!dv.length){
10915             return;
10916         }
10917         
10918         var choice = this.choices.createChild({
10919             tag: 'li',
10920             cls: 'select2-search-choice',
10921             cn: [
10922                 {
10923                     tag: 'div',
10924                     html: dv
10925                 },
10926                 {
10927                     tag: 'a',
10928                     href: '#',
10929                     cls: 'select2-search-choice-close',
10930                     tabindex: '-1'
10931                 }
10932             ]
10933             
10934         }, this.searchField);
10935         
10936         var close = choice.select('a.select2-search-choice-close', true).first()
10937         
10938         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
10939         
10940         this.item.push(o);
10941         this.lastData = o;
10942         
10943         this.syncValue();
10944         
10945         this.inputEl().dom.value = '';
10946         
10947     },
10948     
10949     onRemoveItem : function(e, _self, o)
10950     {
10951         e.preventDefault();
10952         var index = this.item.indexOf(o.data) * 1;
10953         
10954         if( index < 0){
10955             Roo.log('not this item?!');
10956             return;
10957         }
10958         
10959         this.item.splice(index, 1);
10960         o.item.remove();
10961         
10962         this.syncValue();
10963         
10964         this.fireEvent('remove', this, e);
10965         
10966     },
10967     
10968     syncValue : function()
10969     {
10970         if(!this.item.length){
10971             this.clearValue();
10972             return;
10973         }
10974             
10975         var value = [];
10976         var _this = this;
10977         Roo.each(this.item, function(i){
10978             if(_this.valueField){
10979                 value.push(i[_this.valueField]);
10980                 return;
10981             }
10982
10983             value.push(i);
10984         });
10985
10986         this.value = value.join(',');
10987
10988         if(this.hiddenField){
10989             this.hiddenField.dom.value = this.value;
10990         }
10991     },
10992     
10993     clearItem : function()
10994     {
10995         if(!this.multiple){
10996             return;
10997         }
10998         
10999         this.item = [];
11000         
11001         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11002            c.remove();
11003         });
11004         
11005         this.syncValue();
11006     }
11007     
11008     
11009
11010     /** 
11011     * @cfg {Boolean} grow 
11012     * @hide 
11013     */
11014     /** 
11015     * @cfg {Number} growMin 
11016     * @hide 
11017     */
11018     /** 
11019     * @cfg {Number} growMax 
11020     * @hide 
11021     */
11022     /**
11023      * @hide
11024      * @method autoSize
11025      */
11026 });
11027 /*
11028  * Based on:
11029  * Ext JS Library 1.1.1
11030  * Copyright(c) 2006-2007, Ext JS, LLC.
11031  *
11032  * Originally Released Under LGPL - original licence link has changed is not relivant.
11033  *
11034  * Fork - LGPL
11035  * <script type="text/javascript">
11036  */
11037
11038 /**
11039  * @class Roo.View
11040  * @extends Roo.util.Observable
11041  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11042  * This class also supports single and multi selection modes. <br>
11043  * Create a data model bound view:
11044  <pre><code>
11045  var store = new Roo.data.Store(...);
11046
11047  var view = new Roo.View({
11048     el : "my-element",
11049     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11050  
11051     singleSelect: true,
11052     selectedClass: "ydataview-selected",
11053     store: store
11054  });
11055
11056  // listen for node click?
11057  view.on("click", function(vw, index, node, e){
11058  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11059  });
11060
11061  // load XML data
11062  dataModel.load("foobar.xml");
11063  </code></pre>
11064  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11065  * <br><br>
11066  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11067  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11068  * 
11069  * Note: old style constructor is still suported (container, template, config)
11070  * 
11071  * @constructor
11072  * Create a new View
11073  * @param {Object} config The config object
11074  * 
11075  */
11076 Roo.View = function(config, depreciated_tpl, depreciated_config){
11077     
11078     if (typeof(depreciated_tpl) == 'undefined') {
11079         // new way.. - universal constructor.
11080         Roo.apply(this, config);
11081         this.el  = Roo.get(this.el);
11082     } else {
11083         // old format..
11084         this.el  = Roo.get(config);
11085         this.tpl = depreciated_tpl;
11086         Roo.apply(this, depreciated_config);
11087     }
11088     this.wrapEl  = this.el.wrap().wrap();
11089     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11090     
11091     
11092     if(typeof(this.tpl) == "string"){
11093         this.tpl = new Roo.Template(this.tpl);
11094     } else {
11095         // support xtype ctors..
11096         this.tpl = new Roo.factory(this.tpl, Roo);
11097     }
11098     
11099     
11100     this.tpl.compile();
11101    
11102   
11103     
11104      
11105     /** @private */
11106     this.addEvents({
11107         /**
11108          * @event beforeclick
11109          * Fires before a click is processed. Returns false to cancel the default action.
11110          * @param {Roo.View} this
11111          * @param {Number} index The index of the target node
11112          * @param {HTMLElement} node The target node
11113          * @param {Roo.EventObject} e The raw event object
11114          */
11115             "beforeclick" : true,
11116         /**
11117          * @event click
11118          * Fires when a template node is clicked.
11119          * @param {Roo.View} this
11120          * @param {Number} index The index of the target node
11121          * @param {HTMLElement} node The target node
11122          * @param {Roo.EventObject} e The raw event object
11123          */
11124             "click" : true,
11125         /**
11126          * @event dblclick
11127          * Fires when a template node is double clicked.
11128          * @param {Roo.View} this
11129          * @param {Number} index The index of the target node
11130          * @param {HTMLElement} node The target node
11131          * @param {Roo.EventObject} e The raw event object
11132          */
11133             "dblclick" : true,
11134         /**
11135          * @event contextmenu
11136          * Fires when a template node is right clicked.
11137          * @param {Roo.View} this
11138          * @param {Number} index The index of the target node
11139          * @param {HTMLElement} node The target node
11140          * @param {Roo.EventObject} e The raw event object
11141          */
11142             "contextmenu" : true,
11143         /**
11144          * @event selectionchange
11145          * Fires when the selected nodes change.
11146          * @param {Roo.View} this
11147          * @param {Array} selections Array of the selected nodes
11148          */
11149             "selectionchange" : true,
11150     
11151         /**
11152          * @event beforeselect
11153          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
11154          * @param {Roo.View} this
11155          * @param {HTMLElement} node The node to be selected
11156          * @param {Array} selections Array of currently selected nodes
11157          */
11158             "beforeselect" : true,
11159         /**
11160          * @event preparedata
11161          * Fires on every row to render, to allow you to change the data.
11162          * @param {Roo.View} this
11163          * @param {Object} data to be rendered (change this)
11164          */
11165           "preparedata" : true
11166           
11167           
11168         });
11169
11170
11171
11172     this.el.on({
11173         "click": this.onClick,
11174         "dblclick": this.onDblClick,
11175         "contextmenu": this.onContextMenu,
11176         scope:this
11177     });
11178
11179     this.selections = [];
11180     this.nodes = [];
11181     this.cmp = new Roo.CompositeElementLite([]);
11182     if(this.store){
11183         this.store = Roo.factory(this.store, Roo.data);
11184         this.setStore(this.store, true);
11185     }
11186     
11187     if ( this.footer && this.footer.xtype) {
11188            
11189          var fctr = this.wrapEl.appendChild(document.createElement("div"));
11190         
11191         this.footer.dataSource = this.store
11192         this.footer.container = fctr;
11193         this.footer = Roo.factory(this.footer, Roo);
11194         fctr.insertFirst(this.el);
11195         
11196         // this is a bit insane - as the paging toolbar seems to detach the el..
11197 //        dom.parentNode.parentNode.parentNode
11198          // they get detached?
11199     }
11200     
11201     
11202     Roo.View.superclass.constructor.call(this);
11203     
11204     
11205 };
11206
11207 Roo.extend(Roo.View, Roo.util.Observable, {
11208     
11209      /**
11210      * @cfg {Roo.data.Store} store Data store to load data from.
11211      */
11212     store : false,
11213     
11214     /**
11215      * @cfg {String|Roo.Element} el The container element.
11216      */
11217     el : '',
11218     
11219     /**
11220      * @cfg {String|Roo.Template} tpl The template used by this View 
11221      */
11222     tpl : false,
11223     /**
11224      * @cfg {String} dataName the named area of the template to use as the data area
11225      *                          Works with domtemplates roo-name="name"
11226      */
11227     dataName: false,
11228     /**
11229      * @cfg {String} selectedClass The css class to add to selected nodes
11230      */
11231     selectedClass : "x-view-selected",
11232      /**
11233      * @cfg {String} emptyText The empty text to show when nothing is loaded.
11234      */
11235     emptyText : "",
11236     
11237     /**
11238      * @cfg {String} text to display on mask (default Loading)
11239      */
11240     mask : false,
11241     /**
11242      * @cfg {Boolean} multiSelect Allow multiple selection
11243      */
11244     multiSelect : false,
11245     /**
11246      * @cfg {Boolean} singleSelect Allow single selection
11247      */
11248     singleSelect:  false,
11249     
11250     /**
11251      * @cfg {Boolean} toggleSelect - selecting 
11252      */
11253     toggleSelect : false,
11254     
11255     /**
11256      * Returns the element this view is bound to.
11257      * @return {Roo.Element}
11258      */
11259     getEl : function(){
11260         return this.wrapEl;
11261     },
11262     
11263     
11264
11265     /**
11266      * Refreshes the view. - called by datachanged on the store. - do not call directly.
11267      */
11268     refresh : function(){
11269         Roo.log('refresh');
11270         var t = this.tpl;
11271         
11272         // if we are using something like 'domtemplate', then
11273         // the what gets used is:
11274         // t.applySubtemplate(NAME, data, wrapping data..)
11275         // the outer template then get' applied with
11276         //     the store 'extra data'
11277         // and the body get's added to the
11278         //      roo-name="data" node?
11279         //      <span class='roo-tpl-{name}'></span> ?????
11280         
11281         
11282         
11283         this.clearSelections();
11284         this.el.update("");
11285         var html = [];
11286         var records = this.store.getRange();
11287         if(records.length < 1) {
11288             
11289             // is this valid??  = should it render a template??
11290             
11291             this.el.update(this.emptyText);
11292             return;
11293         }
11294         var el = this.el;
11295         if (this.dataName) {
11296             this.el.update(t.apply(this.store.meta)); //????
11297             el = this.el.child('.roo-tpl-' + this.dataName);
11298         }
11299         
11300         for(var i = 0, len = records.length; i < len; i++){
11301             var data = this.prepareData(records[i].data, i, records[i]);
11302             this.fireEvent("preparedata", this, data, i, records[i]);
11303             html[html.length] = Roo.util.Format.trim(
11304                 this.dataName ?
11305                     t.applySubtemplate(this.dataName, data, this.store.meta) :
11306                     t.apply(data)
11307             );
11308         }
11309         
11310         
11311         
11312         el.update(html.join(""));
11313         this.nodes = el.dom.childNodes;
11314         this.updateIndexes(0);
11315     },
11316     
11317
11318     /**
11319      * Function to override to reformat the data that is sent to
11320      * the template for each node.
11321      * DEPRICATED - use the preparedata event handler.
11322      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
11323      * a JSON object for an UpdateManager bound view).
11324      */
11325     prepareData : function(data, index, record)
11326     {
11327         this.fireEvent("preparedata", this, data, index, record);
11328         return data;
11329     },
11330
11331     onUpdate : function(ds, record){
11332          Roo.log('on update');   
11333         this.clearSelections();
11334         var index = this.store.indexOf(record);
11335         var n = this.nodes[index];
11336         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
11337         n.parentNode.removeChild(n);
11338         this.updateIndexes(index, index);
11339     },
11340
11341     
11342     
11343 // --------- FIXME     
11344     onAdd : function(ds, records, index)
11345     {
11346         Roo.log(['on Add', ds, records, index] );        
11347         this.clearSelections();
11348         if(this.nodes.length == 0){
11349             this.refresh();
11350             return;
11351         }
11352         var n = this.nodes[index];
11353         for(var i = 0, len = records.length; i < len; i++){
11354             var d = this.prepareData(records[i].data, i, records[i]);
11355             if(n){
11356                 this.tpl.insertBefore(n, d);
11357             }else{
11358                 
11359                 this.tpl.append(this.el, d);
11360             }
11361         }
11362         this.updateIndexes(index);
11363     },
11364
11365     onRemove : function(ds, record, index){
11366         Roo.log('onRemove');
11367         this.clearSelections();
11368         var el = this.dataName  ?
11369             this.el.child('.roo-tpl-' + this.dataName) :
11370             this.el; 
11371         
11372         el.dom.removeChild(this.nodes[index]);
11373         this.updateIndexes(index);
11374     },
11375
11376     /**
11377      * Refresh an individual node.
11378      * @param {Number} index
11379      */
11380     refreshNode : function(index){
11381         this.onUpdate(this.store, this.store.getAt(index));
11382     },
11383
11384     updateIndexes : function(startIndex, endIndex){
11385         var ns = this.nodes;
11386         startIndex = startIndex || 0;
11387         endIndex = endIndex || ns.length - 1;
11388         for(var i = startIndex; i <= endIndex; i++){
11389             ns[i].nodeIndex = i;
11390         }
11391     },
11392
11393     /**
11394      * Changes the data store this view uses and refresh the view.
11395      * @param {Store} store
11396      */
11397     setStore : function(store, initial){
11398         if(!initial && this.store){
11399             this.store.un("datachanged", this.refresh);
11400             this.store.un("add", this.onAdd);
11401             this.store.un("remove", this.onRemove);
11402             this.store.un("update", this.onUpdate);
11403             this.store.un("clear", this.refresh);
11404             this.store.un("beforeload", this.onBeforeLoad);
11405             this.store.un("load", this.onLoad);
11406             this.store.un("loadexception", this.onLoad);
11407         }
11408         if(store){
11409           
11410             store.on("datachanged", this.refresh, this);
11411             store.on("add", this.onAdd, this);
11412             store.on("remove", this.onRemove, this);
11413             store.on("update", this.onUpdate, this);
11414             store.on("clear", this.refresh, this);
11415             store.on("beforeload", this.onBeforeLoad, this);
11416             store.on("load", this.onLoad, this);
11417             store.on("loadexception", this.onLoad, this);
11418         }
11419         
11420         if(store){
11421             this.refresh();
11422         }
11423     },
11424     /**
11425      * onbeforeLoad - masks the loading area.
11426      *
11427      */
11428     onBeforeLoad : function(store,opts)
11429     {
11430          Roo.log('onBeforeLoad');   
11431         if (!opts.add) {
11432             this.el.update("");
11433         }
11434         this.el.mask(this.mask ? this.mask : "Loading" ); 
11435     },
11436     onLoad : function ()
11437     {
11438         this.el.unmask();
11439     },
11440     
11441
11442     /**
11443      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
11444      * @param {HTMLElement} node
11445      * @return {HTMLElement} The template node
11446      */
11447     findItemFromChild : function(node){
11448         var el = this.dataName  ?
11449             this.el.child('.roo-tpl-' + this.dataName,true) :
11450             this.el.dom; 
11451         
11452         if(!node || node.parentNode == el){
11453                     return node;
11454             }
11455             var p = node.parentNode;
11456             while(p && p != el){
11457             if(p.parentNode == el){
11458                 return p;
11459             }
11460             p = p.parentNode;
11461         }
11462             return null;
11463     },
11464
11465     /** @ignore */
11466     onClick : function(e){
11467         var item = this.findItemFromChild(e.getTarget());
11468         if(item){
11469             var index = this.indexOf(item);
11470             if(this.onItemClick(item, index, e) !== false){
11471                 this.fireEvent("click", this, index, item, e);
11472             }
11473         }else{
11474             this.clearSelections();
11475         }
11476     },
11477
11478     /** @ignore */
11479     onContextMenu : function(e){
11480         var item = this.findItemFromChild(e.getTarget());
11481         if(item){
11482             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
11483         }
11484     },
11485
11486     /** @ignore */
11487     onDblClick : function(e){
11488         var item = this.findItemFromChild(e.getTarget());
11489         if(item){
11490             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
11491         }
11492     },
11493
11494     onItemClick : function(item, index, e)
11495     {
11496         if(this.fireEvent("beforeclick", this, index, item, e) === false){
11497             return false;
11498         }
11499         if (this.toggleSelect) {
11500             var m = this.isSelected(item) ? 'unselect' : 'select';
11501             Roo.log(m);
11502             var _t = this;
11503             _t[m](item, true, false);
11504             return true;
11505         }
11506         if(this.multiSelect || this.singleSelect){
11507             if(this.multiSelect && e.shiftKey && this.lastSelection){
11508                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
11509             }else{
11510                 this.select(item, this.multiSelect && e.ctrlKey);
11511                 this.lastSelection = item;
11512             }
11513             e.preventDefault();
11514         }
11515         return true;
11516     },
11517
11518     /**
11519      * Get the number of selected nodes.
11520      * @return {Number}
11521      */
11522     getSelectionCount : function(){
11523         return this.selections.length;
11524     },
11525
11526     /**
11527      * Get the currently selected nodes.
11528      * @return {Array} An array of HTMLElements
11529      */
11530     getSelectedNodes : function(){
11531         return this.selections;
11532     },
11533
11534     /**
11535      * Get the indexes of the selected nodes.
11536      * @return {Array}
11537      */
11538     getSelectedIndexes : function(){
11539         var indexes = [], s = this.selections;
11540         for(var i = 0, len = s.length; i < len; i++){
11541             indexes.push(s[i].nodeIndex);
11542         }
11543         return indexes;
11544     },
11545
11546     /**
11547      * Clear all selections
11548      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
11549      */
11550     clearSelections : function(suppressEvent){
11551         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
11552             this.cmp.elements = this.selections;
11553             this.cmp.removeClass(this.selectedClass);
11554             this.selections = [];
11555             if(!suppressEvent){
11556                 this.fireEvent("selectionchange", this, this.selections);
11557             }
11558         }
11559     },
11560
11561     /**
11562      * Returns true if the passed node is selected
11563      * @param {HTMLElement/Number} node The node or node index
11564      * @return {Boolean}
11565      */
11566     isSelected : function(node){
11567         var s = this.selections;
11568         if(s.length < 1){
11569             return false;
11570         }
11571         node = this.getNode(node);
11572         return s.indexOf(node) !== -1;
11573     },
11574
11575     /**
11576      * Selects nodes.
11577      * @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
11578      * @param {Boolean} keepExisting (optional) true to keep existing selections
11579      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
11580      */
11581     select : function(nodeInfo, keepExisting, suppressEvent){
11582         if(nodeInfo instanceof Array){
11583             if(!keepExisting){
11584                 this.clearSelections(true);
11585             }
11586             for(var i = 0, len = nodeInfo.length; i < len; i++){
11587                 this.select(nodeInfo[i], true, true);
11588             }
11589             return;
11590         } 
11591         var node = this.getNode(nodeInfo);
11592         if(!node || this.isSelected(node)){
11593             return; // already selected.
11594         }
11595         if(!keepExisting){
11596             this.clearSelections(true);
11597         }
11598         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
11599             Roo.fly(node).addClass(this.selectedClass);
11600             this.selections.push(node);
11601             if(!suppressEvent){
11602                 this.fireEvent("selectionchange", this, this.selections);
11603             }
11604         }
11605         
11606         
11607     },
11608       /**
11609      * Unselects nodes.
11610      * @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
11611      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
11612      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
11613      */
11614     unselect : function(nodeInfo, keepExisting, suppressEvent)
11615     {
11616         if(nodeInfo instanceof Array){
11617             Roo.each(this.selections, function(s) {
11618                 this.unselect(s, nodeInfo);
11619             }, this);
11620             return;
11621         }
11622         var node = this.getNode(nodeInfo);
11623         if(!node || !this.isSelected(node)){
11624             Roo.log("not selected");
11625             return; // not selected.
11626         }
11627         // fireevent???
11628         var ns = [];
11629         Roo.each(this.selections, function(s) {
11630             if (s == node ) {
11631                 Roo.fly(node).removeClass(this.selectedClass);
11632
11633                 return;
11634             }
11635             ns.push(s);
11636         },this);
11637         
11638         this.selections= ns;
11639         this.fireEvent("selectionchange", this, this.selections);
11640     },
11641
11642     /**
11643      * Gets a template node.
11644      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
11645      * @return {HTMLElement} The node or null if it wasn't found
11646      */
11647     getNode : function(nodeInfo){
11648         if(typeof nodeInfo == "string"){
11649             return document.getElementById(nodeInfo);
11650         }else if(typeof nodeInfo == "number"){
11651             return this.nodes[nodeInfo];
11652         }
11653         return nodeInfo;
11654     },
11655
11656     /**
11657      * Gets a range template nodes.
11658      * @param {Number} startIndex
11659      * @param {Number} endIndex
11660      * @return {Array} An array of nodes
11661      */
11662     getNodes : function(start, end){
11663         var ns = this.nodes;
11664         start = start || 0;
11665         end = typeof end == "undefined" ? ns.length - 1 : end;
11666         var nodes = [];
11667         if(start <= end){
11668             for(var i = start; i <= end; i++){
11669                 nodes.push(ns[i]);
11670             }
11671         } else{
11672             for(var i = start; i >= end; i--){
11673                 nodes.push(ns[i]);
11674             }
11675         }
11676         return nodes;
11677     },
11678
11679     /**
11680      * Finds the index of the passed node
11681      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
11682      * @return {Number} The index of the node or -1
11683      */
11684     indexOf : function(node){
11685         node = this.getNode(node);
11686         if(typeof node.nodeIndex == "number"){
11687             return node.nodeIndex;
11688         }
11689         var ns = this.nodes;
11690         for(var i = 0, len = ns.length; i < len; i++){
11691             if(ns[i] == node){
11692                 return i;
11693             }
11694         }
11695         return -1;
11696     }
11697 });
11698 /*
11699  * - LGPL
11700  *
11701  * based on jquery fullcalendar
11702  * 
11703  */
11704
11705 Roo.bootstrap = Roo.bootstrap || {};
11706 /**
11707  * @class Roo.bootstrap.Calendar
11708  * @extends Roo.bootstrap.Component
11709  * Bootstrap Calendar class
11710  * @cfg {Boolean} loadMask (true|false) default false
11711  * @cfg {Object} header generate the user specific header of the calendar, default false
11712
11713  * @constructor
11714  * Create a new Container
11715  * @param {Object} config The config object
11716  */
11717
11718
11719
11720 Roo.bootstrap.Calendar = function(config){
11721     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
11722      this.addEvents({
11723         /**
11724              * @event select
11725              * Fires when a date is selected
11726              * @param {DatePicker} this
11727              * @param {Date} date The selected date
11728              */
11729         'select': true,
11730         /**
11731              * @event monthchange
11732              * Fires when the displayed month changes 
11733              * @param {DatePicker} this
11734              * @param {Date} date The selected month
11735              */
11736         'monthchange': true,
11737         /**
11738              * @event evententer
11739              * Fires when mouse over an event
11740              * @param {Calendar} this
11741              * @param {event} Event
11742              */
11743         'evententer': true,
11744         /**
11745              * @event eventleave
11746              * Fires when the mouse leaves an
11747              * @param {Calendar} this
11748              * @param {event}
11749              */
11750         'eventleave': true,
11751         /**
11752              * @event eventclick
11753              * Fires when the mouse click an
11754              * @param {Calendar} this
11755              * @param {event}
11756              */
11757         'eventclick': true
11758         
11759     });
11760
11761 };
11762
11763 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
11764     
11765      /**
11766      * @cfg {Number} startDay
11767      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
11768      */
11769     startDay : 0,
11770     
11771     loadMask : false,
11772     
11773     header : false,
11774       
11775     getAutoCreate : function(){
11776         
11777         
11778         var fc_button = function(name, corner, style, content ) {
11779             return Roo.apply({},{
11780                 tag : 'span',
11781                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
11782                          (corner.length ?
11783                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
11784                             ''
11785                         ),
11786                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
11787                 unselectable: 'on'
11788             });
11789         };
11790         
11791         var header = {};
11792         
11793         if(!this.header){
11794             header = {
11795                 tag : 'table',
11796                 cls : 'fc-header',
11797                 style : 'width:100%',
11798                 cn : [
11799                     {
11800                         tag: 'tr',
11801                         cn : [
11802                             {
11803                                 tag : 'td',
11804                                 cls : 'fc-header-left',
11805                                 cn : [
11806                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
11807                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
11808                                     { tag: 'span', cls: 'fc-header-space' },
11809                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
11810
11811
11812                                 ]
11813                             },
11814
11815                             {
11816                                 tag : 'td',
11817                                 cls : 'fc-header-center',
11818                                 cn : [
11819                                     {
11820                                         tag: 'span',
11821                                         cls: 'fc-header-title',
11822                                         cn : {
11823                                             tag: 'H2',
11824                                             html : 'month / year'
11825                                         }
11826                                     }
11827
11828                                 ]
11829                             },
11830                             {
11831                                 tag : 'td',
11832                                 cls : 'fc-header-right',
11833                                 cn : [
11834                               /*      fc_button('month', 'left', '', 'month' ),
11835                                     fc_button('week', '', '', 'week' ),
11836                                     fc_button('day', 'right', '', 'day' )
11837                                 */    
11838
11839                                 ]
11840                             }
11841
11842                         ]
11843                     }
11844                 ]
11845             };
11846         }
11847         
11848         header = this.header;
11849         
11850        
11851         var cal_heads = function() {
11852             var ret = [];
11853             // fixme - handle this.
11854             
11855             for (var i =0; i < Date.dayNames.length; i++) {
11856                 var d = Date.dayNames[i];
11857                 ret.push({
11858                     tag: 'th',
11859                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
11860                     html : d.substring(0,3)
11861                 });
11862                 
11863             }
11864             ret[0].cls += ' fc-first';
11865             ret[6].cls += ' fc-last';
11866             return ret;
11867         };
11868         var cal_cell = function(n) {
11869             return  {
11870                 tag: 'td',
11871                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
11872                 cn : [
11873                     {
11874                         cn : [
11875                             {
11876                                 cls: 'fc-day-number',
11877                                 html: 'D'
11878                             },
11879                             {
11880                                 cls: 'fc-day-content',
11881                              
11882                                 cn : [
11883                                      {
11884                                         style: 'position: relative;' // height: 17px;
11885                                     }
11886                                 ]
11887                             }
11888                             
11889                             
11890                         ]
11891                     }
11892                 ]
11893                 
11894             }
11895         };
11896         var cal_rows = function() {
11897             
11898             var ret = []
11899             for (var r = 0; r < 6; r++) {
11900                 var row= {
11901                     tag : 'tr',
11902                     cls : 'fc-week',
11903                     cn : []
11904                 };
11905                 
11906                 for (var i =0; i < Date.dayNames.length; i++) {
11907                     var d = Date.dayNames[i];
11908                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
11909
11910                 }
11911                 row.cn[0].cls+=' fc-first';
11912                 row.cn[0].cn[0].style = 'min-height:90px';
11913                 row.cn[6].cls+=' fc-last';
11914                 ret.push(row);
11915                 
11916             }
11917             ret[0].cls += ' fc-first';
11918             ret[4].cls += ' fc-prev-last';
11919             ret[5].cls += ' fc-last';
11920             return ret;
11921             
11922         };
11923         
11924         var cal_table = {
11925             tag: 'table',
11926             cls: 'fc-border-separate',
11927             style : 'width:100%',
11928             cellspacing  : 0,
11929             cn : [
11930                 { 
11931                     tag: 'thead',
11932                     cn : [
11933                         { 
11934                             tag: 'tr',
11935                             cls : 'fc-first fc-last',
11936                             cn : cal_heads()
11937                         }
11938                     ]
11939                 },
11940                 { 
11941                     tag: 'tbody',
11942                     cn : cal_rows()
11943                 }
11944                   
11945             ]
11946         };
11947          
11948          var cfg = {
11949             cls : 'fc fc-ltr',
11950             cn : [
11951                 header,
11952                 {
11953                     cls : 'fc-content',
11954                     style : "position: relative;",
11955                     cn : [
11956                         {
11957                             cls : 'fc-view fc-view-month fc-grid',
11958                             style : 'position: relative',
11959                             unselectable : 'on',
11960                             cn : [
11961                                 {
11962                                     cls : 'fc-event-container',
11963                                     style : 'position:absolute;z-index:8;top:0;left:0;'
11964                                 },
11965                                 cal_table
11966                             ]
11967                         }
11968                     ]
11969     
11970                 }
11971            ] 
11972             
11973         };
11974         
11975          
11976         
11977         return cfg;
11978     },
11979     
11980     
11981     initEvents : function()
11982     {
11983         if(!this.store){
11984             throw "can not find store for calendar";
11985         }
11986         
11987         var mark = {
11988             tag: "div",
11989             cls:"x-dlg-mask",
11990             style: "text-align:center",
11991             cn: [
11992                 {
11993                     tag: "div",
11994                     style: "background-color:white;width:50%;margin:250 auto",
11995                     cn: [
11996                         {
11997                             tag: "img",
11998                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
11999                         },
12000                         {
12001                             tag: "span",
12002                             html: "Loading"
12003                         }
12004                         
12005                     ]
12006                 }
12007             ]
12008         }
12009         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12010         
12011         var size = this.el.select('.fc-content', true).first().getSize();
12012         this.maskEl.setSize(size.width, size.height);
12013         this.maskEl.enableDisplayMode("block");
12014         if(!this.loadMask){
12015             this.maskEl.hide();
12016         }
12017         
12018         this.store = Roo.factory(this.store, Roo.data);
12019         this.store.on('load', this.onLoad, this);
12020         this.store.on('beforeload', this.onBeforeLoad, this);
12021         
12022         this.resize();
12023         
12024         this.cells = this.el.select('.fc-day',true);
12025         //Roo.log(this.cells);
12026         this.textNodes = this.el.query('.fc-day-number');
12027         this.cells.addClassOnOver('fc-state-hover');
12028         
12029         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12030         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12031         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12032         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12033         
12034         this.on('monthchange', this.onMonthChange, this);
12035         
12036         this.update(new Date().clearTime());
12037     },
12038     
12039     resize : function() {
12040         var sz  = this.el.getSize();
12041         
12042         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12043         this.el.select('.fc-day-content div',true).setHeight(34);
12044     },
12045     
12046     
12047     // private
12048     showPrevMonth : function(e){
12049         this.update(this.activeDate.add("mo", -1));
12050     },
12051     showToday : function(e){
12052         this.update(new Date().clearTime());
12053     },
12054     // private
12055     showNextMonth : function(e){
12056         this.update(this.activeDate.add("mo", 1));
12057     },
12058
12059     // private
12060     showPrevYear : function(){
12061         this.update(this.activeDate.add("y", -1));
12062     },
12063
12064     // private
12065     showNextYear : function(){
12066         this.update(this.activeDate.add("y", 1));
12067     },
12068
12069     
12070    // private
12071     update : function(date)
12072     {
12073         var vd = this.activeDate;
12074         this.activeDate = date;
12075 //        if(vd && this.el){
12076 //            var t = date.getTime();
12077 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12078 //                Roo.log('using add remove');
12079 //                
12080 //                this.fireEvent('monthchange', this, date);
12081 //                
12082 //                this.cells.removeClass("fc-state-highlight");
12083 //                this.cells.each(function(c){
12084 //                   if(c.dateValue == t){
12085 //                       c.addClass("fc-state-highlight");
12086 //                       setTimeout(function(){
12087 //                            try{c.dom.firstChild.focus();}catch(e){}
12088 //                       }, 50);
12089 //                       return false;
12090 //                   }
12091 //                   return true;
12092 //                });
12093 //                return;
12094 //            }
12095 //        }
12096         
12097         var days = date.getDaysInMonth();
12098         
12099         var firstOfMonth = date.getFirstDateOfMonth();
12100         var startingPos = firstOfMonth.getDay()-this.startDay;
12101         
12102         if(startingPos < this.startDay){
12103             startingPos += 7;
12104         }
12105         
12106         var pm = date.add(Date.MONTH, -1);
12107         var prevStart = pm.getDaysInMonth()-startingPos;
12108 //        
12109         this.cells = this.el.select('.fc-day',true);
12110         this.textNodes = this.el.query('.fc-day-number');
12111         this.cells.addClassOnOver('fc-state-hover');
12112         
12113         var cells = this.cells.elements;
12114         var textEls = this.textNodes;
12115         
12116         Roo.each(cells, function(cell){
12117             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
12118         });
12119         
12120         days += startingPos;
12121
12122         // convert everything to numbers so it's fast
12123         var day = 86400000;
12124         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
12125         //Roo.log(d);
12126         //Roo.log(pm);
12127         //Roo.log(prevStart);
12128         
12129         var today = new Date().clearTime().getTime();
12130         var sel = date.clearTime().getTime();
12131         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
12132         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
12133         var ddMatch = this.disabledDatesRE;
12134         var ddText = this.disabledDatesText;
12135         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
12136         var ddaysText = this.disabledDaysText;
12137         var format = this.format;
12138         
12139         var setCellClass = function(cal, cell){
12140             cell.row = 0;
12141             cell.events = [];
12142             cell.more = [];
12143             //Roo.log('set Cell Class');
12144             cell.title = "";
12145             var t = d.getTime();
12146             
12147             //Roo.log(d);
12148             
12149             cell.dateValue = t;
12150             if(t == today){
12151                 cell.className += " fc-today";
12152                 cell.className += " fc-state-highlight";
12153                 cell.title = cal.todayText;
12154             }
12155             if(t == sel){
12156                 // disable highlight in other month..
12157                 //cell.className += " fc-state-highlight";
12158                 
12159             }
12160             // disabling
12161             if(t < min) {
12162                 cell.className = " fc-state-disabled";
12163                 cell.title = cal.minText;
12164                 return;
12165             }
12166             if(t > max) {
12167                 cell.className = " fc-state-disabled";
12168                 cell.title = cal.maxText;
12169                 return;
12170             }
12171             if(ddays){
12172                 if(ddays.indexOf(d.getDay()) != -1){
12173                     cell.title = ddaysText;
12174                     cell.className = " fc-state-disabled";
12175                 }
12176             }
12177             if(ddMatch && format){
12178                 var fvalue = d.dateFormat(format);
12179                 if(ddMatch.test(fvalue)){
12180                     cell.title = ddText.replace("%0", fvalue);
12181                     cell.className = " fc-state-disabled";
12182                 }
12183             }
12184             
12185             if (!cell.initialClassName) {
12186                 cell.initialClassName = cell.dom.className;
12187             }
12188             
12189             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
12190         };
12191
12192         var i = 0;
12193         
12194         for(; i < startingPos; i++) {
12195             textEls[i].innerHTML = (++prevStart);
12196             d.setDate(d.getDate()+1);
12197             
12198             cells[i].className = "fc-past fc-other-month";
12199             setCellClass(this, cells[i]);
12200         }
12201         
12202         var intDay = 0;
12203         
12204         for(; i < days; i++){
12205             intDay = i - startingPos + 1;
12206             textEls[i].innerHTML = (intDay);
12207             d.setDate(d.getDate()+1);
12208             
12209             cells[i].className = ''; // "x-date-active";
12210             setCellClass(this, cells[i]);
12211         }
12212         var extraDays = 0;
12213         
12214         for(; i < 42; i++) {
12215             textEls[i].innerHTML = (++extraDays);
12216             d.setDate(d.getDate()+1);
12217             
12218             cells[i].className = "fc-future fc-other-month";
12219             setCellClass(this, cells[i]);
12220         }
12221         
12222         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
12223         
12224         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
12225         
12226         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
12227         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
12228         
12229         if(totalRows != 6){
12230             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
12231             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
12232         }
12233         
12234         this.fireEvent('monthchange', this, date);
12235         
12236         
12237         /*
12238         if(!this.internalRender){
12239             var main = this.el.dom.firstChild;
12240             var w = main.offsetWidth;
12241             this.el.setWidth(w + this.el.getBorderWidth("lr"));
12242             Roo.fly(main).setWidth(w);
12243             this.internalRender = true;
12244             // opera does not respect the auto grow header center column
12245             // then, after it gets a width opera refuses to recalculate
12246             // without a second pass
12247             if(Roo.isOpera && !this.secondPass){
12248                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
12249                 this.secondPass = true;
12250                 this.update.defer(10, this, [date]);
12251             }
12252         }
12253         */
12254         
12255     },
12256     
12257     findCell : function(dt) {
12258         dt = dt.clearTime().getTime();
12259         var ret = false;
12260         this.cells.each(function(c){
12261             //Roo.log("check " +c.dateValue + '?=' + dt);
12262             if(c.dateValue == dt){
12263                 ret = c;
12264                 return false;
12265             }
12266             return true;
12267         });
12268         
12269         return ret;
12270     },
12271     
12272     findCells : function(ev) {
12273         var s = ev.start.clone().clearTime().getTime();
12274        // Roo.log(s);
12275         var e= ev.end.clone().clearTime().getTime();
12276        // Roo.log(e);
12277         var ret = [];
12278         this.cells.each(function(c){
12279              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
12280             
12281             if(c.dateValue > e){
12282                 return ;
12283             }
12284             if(c.dateValue < s){
12285                 return ;
12286             }
12287             ret.push(c);
12288         });
12289         
12290         return ret;    
12291     },
12292     
12293 //    findBestRow: function(cells)
12294 //    {
12295 //        var ret = 0;
12296 //        
12297 //        for (var i =0 ; i < cells.length;i++) {
12298 //            ret  = Math.max(cells[i].rows || 0,ret);
12299 //        }
12300 //        return ret;
12301 //        
12302 //    },
12303     
12304     
12305     addItem : function(ev)
12306     {
12307         // look for vertical location slot in
12308         var cells = this.findCells(ev);
12309         
12310 //        ev.row = this.findBestRow(cells);
12311         
12312         // work out the location.
12313         
12314         var crow = false;
12315         var rows = [];
12316         for(var i =0; i < cells.length; i++) {
12317             
12318             cells[i].row = cells[0].row;
12319             
12320             if(i == 0){
12321                 cells[i].row = cells[i].row + 1;
12322             }
12323             
12324             if (!crow) {
12325                 crow = {
12326                     start : cells[i],
12327                     end :  cells[i]
12328                 };
12329                 continue;
12330             }
12331             if (crow.start.getY() == cells[i].getY()) {
12332                 // on same row.
12333                 crow.end = cells[i];
12334                 continue;
12335             }
12336             // different row.
12337             rows.push(crow);
12338             crow = {
12339                 start: cells[i],
12340                 end : cells[i]
12341             };
12342             
12343         }
12344         
12345         rows.push(crow);
12346         ev.els = [];
12347         ev.rows = rows;
12348         ev.cells = cells;
12349         
12350         cells[0].events.push(ev);
12351         
12352         this.calevents.push(ev);
12353     },
12354     
12355     clearEvents: function() {
12356         
12357         if(!this.calevents){
12358             return;
12359         }
12360         
12361         Roo.each(this.cells.elements, function(c){
12362             c.row = 0;
12363             c.events = [];
12364             c.more = [];
12365         });
12366         
12367         Roo.each(this.calevents, function(e) {
12368             Roo.each(e.els, function(el) {
12369                 el.un('mouseenter' ,this.onEventEnter, this);
12370                 el.un('mouseleave' ,this.onEventLeave, this);
12371                 el.remove();
12372             },this);
12373         },this);
12374         
12375         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
12376             e.remove();
12377         });
12378         
12379     },
12380     
12381     renderEvents: function()
12382     {   
12383         var _this = this;
12384         
12385         this.cells.each(function(c) {
12386             
12387             if(c.row < 5){
12388                 return;
12389             }
12390             
12391             var ev = c.events;
12392             
12393             var r = 4;
12394             if(c.row != c.events.length){
12395                 r = 4 - (4 - (c.row - c.events.length));
12396             }
12397             
12398             c.events = ev.slice(0, r);
12399             c.more = ev.slice(r);
12400             
12401             if(c.more.length && c.more.length == 1){
12402                 c.events.push(c.more.pop());
12403             }
12404             
12405             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
12406             
12407         });
12408             
12409         this.cells.each(function(c) {
12410             
12411             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
12412             
12413             
12414             for (var e = 0; e < c.events.length; e++){
12415                 var ev = c.events[e];
12416                 var rows = ev.rows;
12417                 
12418                 for(var i = 0; i < rows.length; i++) {
12419                 
12420                     // how many rows should it span..
12421
12422                     var  cfg = {
12423                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
12424                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
12425
12426                         unselectable : "on",
12427                         cn : [
12428                             {
12429                                 cls: 'fc-event-inner',
12430                                 cn : [
12431     //                                {
12432     //                                  tag:'span',
12433     //                                  cls: 'fc-event-time',
12434     //                                  html : cells.length > 1 ? '' : ev.time
12435     //                                },
12436                                     {
12437                                       tag:'span',
12438                                       cls: 'fc-event-title',
12439                                       html : String.format('{0}', ev.title)
12440                                     }
12441
12442
12443                                 ]
12444                             },
12445                             {
12446                                 cls: 'ui-resizable-handle ui-resizable-e',
12447                                 html : '&nbsp;&nbsp;&nbsp'
12448                             }
12449
12450                         ]
12451                     };
12452
12453                     if (i == 0) {
12454                         cfg.cls += ' fc-event-start';
12455                     }
12456                     if ((i+1) == rows.length) {
12457                         cfg.cls += ' fc-event-end';
12458                     }
12459
12460                     var ctr = _this.el.select('.fc-event-container',true).first();
12461                     var cg = ctr.createChild(cfg);
12462
12463                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
12464                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
12465
12466                     var r = (c.more.length) ? 1 : 0;
12467                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
12468                     cg.setWidth(ebox.right - sbox.x -2);
12469
12470                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
12471                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
12472                     cg.on('click', _this.onEventClick, _this, ev);
12473
12474                     ev.els.push(cg);
12475                     
12476                 }
12477                 
12478             }
12479             
12480             
12481             if(c.more.length){
12482                 var  cfg = {
12483                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
12484                     style : 'position: absolute',
12485                     unselectable : "on",
12486                     cn : [
12487                         {
12488                             cls: 'fc-event-inner',
12489                             cn : [
12490                                 {
12491                                   tag:'span',
12492                                   cls: 'fc-event-title',
12493                                   html : 'More'
12494                                 }
12495
12496
12497                             ]
12498                         },
12499                         {
12500                             cls: 'ui-resizable-handle ui-resizable-e',
12501                             html : '&nbsp;&nbsp;&nbsp'
12502                         }
12503
12504                     ]
12505                 };
12506
12507                 var ctr = _this.el.select('.fc-event-container',true).first();
12508                 var cg = ctr.createChild(cfg);
12509
12510                 var sbox = c.select('.fc-day-content',true).first().getBox();
12511                 var ebox = c.select('.fc-day-content',true).first().getBox();
12512                 //Roo.log(cg);
12513                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
12514                 cg.setWidth(ebox.right - sbox.x -2);
12515
12516                 cg.on('click', _this.onMoreEventClick, _this, c.more);
12517                 
12518             }
12519             
12520         });
12521         
12522         
12523         
12524     },
12525     
12526     onEventEnter: function (e, el,event,d) {
12527         this.fireEvent('evententer', this, el, event);
12528     },
12529     
12530     onEventLeave: function (e, el,event,d) {
12531         this.fireEvent('eventleave', this, el, event);
12532     },
12533     
12534     onEventClick: function (e, el,event,d) {
12535         this.fireEvent('eventclick', this, el, event);
12536     },
12537     
12538     onMonthChange: function () {
12539         this.store.load();
12540     },
12541     
12542     onMoreEventClick: function(e, el, more)
12543     {
12544         var _this = this;
12545         
12546         this.calpopover.placement = 'right';
12547         this.calpopover.setTitle('More');
12548         
12549         this.calpopover.setContent('');
12550         
12551         var ctr = this.calpopover.el.select('.popover-content', true).first();
12552         
12553         Roo.each(more, function(m){
12554             var cfg = {
12555                 cls : 'fc-event-hori fc-event-draggable',
12556                 html : m.title
12557             }
12558             var cg = ctr.createChild(cfg);
12559             
12560             cg.on('click', _this.onEventClick, _this, m);
12561         });
12562         
12563         this.calpopover.show(el);
12564         
12565         
12566     },
12567     
12568     onLoad: function () 
12569     {   
12570         this.calevents = [];
12571         var cal = this;
12572         
12573         if(this.store.getCount() > 0){
12574             this.store.data.each(function(d){
12575                cal.addItem({
12576                     id : d.data.id,
12577                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
12578                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
12579                     time : d.data.start_time,
12580                     title : d.data.title,
12581                     description : d.data.description,
12582                     venue : d.data.venue
12583                 });
12584             });
12585         }
12586         
12587         this.renderEvents();
12588         
12589         if(this.calevents.length && this.loadMask){
12590             this.maskEl.hide();
12591         }
12592     },
12593     
12594     onBeforeLoad: function()
12595     {
12596         this.clearEvents();
12597         if(this.loadMask){
12598             this.maskEl.show();
12599         }
12600     }
12601 });
12602
12603  
12604  /*
12605  * - LGPL
12606  *
12607  * element
12608  * 
12609  */
12610
12611 /**
12612  * @class Roo.bootstrap.Popover
12613  * @extends Roo.bootstrap.Component
12614  * Bootstrap Popover class
12615  * @cfg {String} html contents of the popover   (or false to use children..)
12616  * @cfg {String} title of popover (or false to hide)
12617  * @cfg {String} placement how it is placed
12618  * @cfg {String} trigger click || hover (or false to trigger manually)
12619  * @cfg {String} over what (parent or false to trigger manually.)
12620  * 
12621  * @constructor
12622  * Create a new Popover
12623  * @param {Object} config The config object
12624  */
12625
12626 Roo.bootstrap.Popover = function(config){
12627     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
12628 };
12629
12630 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
12631     
12632     title: 'Fill in a title',
12633     html: false,
12634     
12635     placement : 'right',
12636     trigger : 'hover', // hover
12637     
12638     over: 'parent',
12639     
12640     can_build_overlaid : false,
12641     
12642     getChildContainer : function()
12643     {
12644         return this.el.select('.popover-content',true).first();
12645     },
12646     
12647     getAutoCreate : function(){
12648          Roo.log('make popover?');
12649         var cfg = {
12650            cls : 'popover roo-dynamic',
12651            style: 'display:block',
12652            cn : [
12653                 {
12654                     cls : 'arrow'
12655                 },
12656                 {
12657                     cls : 'popover-inner',
12658                     cn : [
12659                         {
12660                             tag: 'h3',
12661                             cls: 'popover-title',
12662                             html : this.title
12663                         },
12664                         {
12665                             cls : 'popover-content',
12666                             html : this.html
12667                         }
12668                     ]
12669                     
12670                 }
12671            ]
12672         };
12673         
12674         return cfg;
12675     },
12676     setTitle: function(str)
12677     {
12678         this.el.select('.popover-title',true).first().dom.innerHTML = str;
12679     },
12680     setContent: function(str)
12681     {
12682         this.el.select('.popover-content',true).first().dom.innerHTML = str;
12683     },
12684     // as it get's added to the bottom of the page.
12685     onRender : function(ct, position)
12686     {
12687         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
12688         if(!this.el){
12689             var cfg = Roo.apply({},  this.getAutoCreate());
12690             cfg.id = Roo.id();
12691             
12692             if (this.cls) {
12693                 cfg.cls += ' ' + this.cls;
12694             }
12695             if (this.style) {
12696                 cfg.style = this.style;
12697             }
12698             Roo.log("adding to ")
12699             this.el = Roo.get(document.body).createChild(cfg, position);
12700             Roo.log(this.el);
12701         }
12702         this.initEvents();
12703     },
12704     
12705     initEvents : function()
12706     {
12707         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
12708         this.el.enableDisplayMode('block');
12709         this.el.hide();
12710         if (this.over === false) {
12711             return; 
12712         }
12713         if (this.triggers === false) {
12714             return;
12715         }
12716         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12717         var triggers = this.trigger ? this.trigger.split(' ') : [];
12718         Roo.each(triggers, function(trigger) {
12719         
12720             if (trigger == 'click') {
12721                 on_el.on('click', this.toggle, this);
12722             } else if (trigger != 'manual') {
12723                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
12724                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
12725       
12726                 on_el.on(eventIn  ,this.enter, this);
12727                 on_el.on(eventOut, this.leave, this);
12728             }
12729         }, this);
12730         
12731     },
12732     
12733     
12734     // private
12735     timeout : null,
12736     hoverState : null,
12737     
12738     toggle : function () {
12739         this.hoverState == 'in' ? this.leave() : this.enter();
12740     },
12741     
12742     enter : function () {
12743        
12744     
12745         clearTimeout(this.timeout);
12746     
12747         this.hoverState = 'in'
12748     
12749         if (!this.delay || !this.delay.show) {
12750             this.show();
12751             return 
12752         }
12753         var _t = this;
12754         this.timeout = setTimeout(function () {
12755             if (_t.hoverState == 'in') {
12756                 _t.show();
12757             }
12758         }, this.delay.show)
12759     },
12760     leave : function() {
12761         clearTimeout(this.timeout);
12762     
12763         this.hoverState = 'out'
12764     
12765         if (!this.delay || !this.delay.hide) {
12766             this.hide();
12767             return 
12768         }
12769         var _t = this;
12770         this.timeout = setTimeout(function () {
12771             if (_t.hoverState == 'out') {
12772                 _t.hide();
12773             }
12774         }, this.delay.hide)
12775     },
12776     
12777     show : function (on_el)
12778     {
12779         if (!on_el) {
12780             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12781         }
12782         // set content.
12783         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
12784         if (this.html !== false) {
12785             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
12786         }
12787         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
12788         if (!this.title.length) {
12789             this.el.select('.popover-title',true).hide();
12790         }
12791         
12792         var placement = typeof this.placement == 'function' ?
12793             this.placement.call(this, this.el, on_el) :
12794             this.placement;
12795             
12796         var autoToken = /\s?auto?\s?/i;
12797         var autoPlace = autoToken.test(placement);
12798         if (autoPlace) {
12799             placement = placement.replace(autoToken, '') || 'top';
12800         }
12801         
12802         //this.el.detach()
12803         //this.el.setXY([0,0]);
12804         this.el.show();
12805         this.el.dom.style.display='block';
12806         this.el.addClass(placement);
12807         
12808         //this.el.appendTo(on_el);
12809         
12810         var p = this.getPosition();
12811         var box = this.el.getBox();
12812         
12813         if (autoPlace) {
12814             // fixme..
12815         }
12816         var align = Roo.bootstrap.Popover.alignment[placement]
12817         this.el.alignTo(on_el, align[0],align[1]);
12818         //var arrow = this.el.select('.arrow',true).first();
12819         //arrow.set(align[2], 
12820         
12821         this.el.addClass('in');
12822         this.hoverState = null;
12823         
12824         if (this.el.hasClass('fade')) {
12825             // fade it?
12826         }
12827         
12828     },
12829     hide : function()
12830     {
12831         this.el.setXY([0,0]);
12832         this.el.removeClass('in');
12833         this.el.hide();
12834         
12835     }
12836     
12837 });
12838
12839 Roo.bootstrap.Popover.alignment = {
12840     'left' : ['r-l', [-10,0], 'right'],
12841     'right' : ['l-r', [10,0], 'left'],
12842     'bottom' : ['t-b', [0,10], 'top'],
12843     'top' : [ 'b-t', [0,-10], 'bottom']
12844 };
12845
12846  /*
12847  * - LGPL
12848  *
12849  * Progress
12850  * 
12851  */
12852
12853 /**
12854  * @class Roo.bootstrap.Progress
12855  * @extends Roo.bootstrap.Component
12856  * Bootstrap Progress class
12857  * @cfg {Boolean} striped striped of the progress bar
12858  * @cfg {Boolean} active animated of the progress bar
12859  * 
12860  * 
12861  * @constructor
12862  * Create a new Progress
12863  * @param {Object} config The config object
12864  */
12865
12866 Roo.bootstrap.Progress = function(config){
12867     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
12868 };
12869
12870 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
12871     
12872     striped : false,
12873     active: false,
12874     
12875     getAutoCreate : function(){
12876         var cfg = {
12877             tag: 'div',
12878             cls: 'progress'
12879         };
12880         
12881         
12882         if(this.striped){
12883             cfg.cls += ' progress-striped';
12884         }
12885       
12886         if(this.active){
12887             cfg.cls += ' active';
12888         }
12889         
12890         
12891         return cfg;
12892     }
12893    
12894 });
12895
12896  
12897
12898  /*
12899  * - LGPL
12900  *
12901  * ProgressBar
12902  * 
12903  */
12904
12905 /**
12906  * @class Roo.bootstrap.ProgressBar
12907  * @extends Roo.bootstrap.Component
12908  * Bootstrap ProgressBar class
12909  * @cfg {Number} aria_valuenow aria-value now
12910  * @cfg {Number} aria_valuemin aria-value min
12911  * @cfg {Number} aria_valuemax aria-value max
12912  * @cfg {String} label label for the progress bar
12913  * @cfg {String} panel (success | info | warning | danger )
12914  * @cfg {String} role role of the progress bar
12915  * @cfg {String} sr_only text
12916  * 
12917  * 
12918  * @constructor
12919  * Create a new ProgressBar
12920  * @param {Object} config The config object
12921  */
12922
12923 Roo.bootstrap.ProgressBar = function(config){
12924     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
12925 };
12926
12927 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
12928     
12929     aria_valuenow : 0,
12930     aria_valuemin : 0,
12931     aria_valuemax : 100,
12932     label : false,
12933     panel : false,
12934     role : false,
12935     sr_only: false,
12936     
12937     getAutoCreate : function()
12938     {
12939         
12940         var cfg = {
12941             tag: 'div',
12942             cls: 'progress-bar',
12943             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
12944         };
12945         
12946         if(this.sr_only){
12947             cfg.cn = {
12948                 tag: 'span',
12949                 cls: 'sr-only',
12950                 html: this.sr_only
12951             }
12952         }
12953         
12954         if(this.role){
12955             cfg.role = this.role;
12956         }
12957         
12958         if(this.aria_valuenow){
12959             cfg['aria-valuenow'] = this.aria_valuenow;
12960         }
12961         
12962         if(this.aria_valuemin){
12963             cfg['aria-valuemin'] = this.aria_valuemin;
12964         }
12965         
12966         if(this.aria_valuemax){
12967             cfg['aria-valuemax'] = this.aria_valuemax;
12968         }
12969         
12970         if(this.label && !this.sr_only){
12971             cfg.html = this.label;
12972         }
12973         
12974         if(this.panel){
12975             cfg.cls += ' progress-bar-' + this.panel;
12976         }
12977         
12978         return cfg;
12979     },
12980     
12981     update : function(aria_valuenow)
12982     {
12983         this.aria_valuenow = aria_valuenow;
12984         
12985         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
12986     }
12987    
12988 });
12989
12990  
12991
12992  /*
12993  * - LGPL
12994  *
12995  * TabPanel
12996  * 
12997  */
12998
12999 /**
13000  * @class Roo.bootstrap.TabPanel
13001  * @extends Roo.bootstrap.Component
13002  * Bootstrap TabPanel class
13003  * @cfg {Boolean} active panel active
13004  * @cfg {String} html panel content
13005  * @cfg {String} tabId tab relate id
13006  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
13007  * 
13008  * 
13009  * @constructor
13010  * Create a new TabPanel
13011  * @param {Object} config The config object
13012  */
13013
13014 Roo.bootstrap.TabPanel = function(config){
13015     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
13016      this.addEvents({
13017         /**
13018              * @event changed
13019              * Fires when the active status changes
13020              * @param {Roo.bootstrap.TabPanel} this
13021              * @param {Boolean} state the new state
13022             
13023          */
13024         'changed': true
13025      });
13026 };
13027
13028 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
13029     
13030     active: false,
13031     html: false,
13032     tabId: false,
13033     navId : false,
13034     
13035     getAutoCreate : function(){
13036         var cfg = {
13037             tag: 'div',
13038             cls: 'tab-pane',
13039             html: this.html || ''
13040         };
13041         
13042         if(this.active){
13043             cfg.cls += ' active';
13044         }
13045         
13046         if(this.tabId){
13047             cfg.tabId = this.tabId;
13048         }
13049         
13050         return cfg;
13051     },
13052     onRender : function(ct, position)
13053     {
13054        // Roo.log("Call onRender: " + this.xtype);
13055         
13056         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
13057         
13058         if (this.navId && this.tabId) {
13059             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
13060             if (!item) {
13061                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
13062             } else {
13063                 item.on('changed', function(item, state) {
13064                     this.setActive(state);
13065                 }, this);
13066             }
13067         }
13068         
13069     },
13070     setActive: function(state)
13071     {
13072         Roo.log("panel - set active " + this.tabId + "=" + state);
13073         
13074         this.active = state;
13075         if (!state) {
13076             this.el.removeClass('active');
13077             
13078         } else  if (!this.el.hasClass('active')) {
13079             this.el.addClass('active');
13080         }
13081         this.fireEvent('changed', this, state);
13082     }
13083     
13084     
13085 });
13086  
13087
13088  
13089
13090  /*
13091  * - LGPL
13092  *
13093  * DateField
13094  * 
13095  */
13096
13097 /**
13098  * @class Roo.bootstrap.DateField
13099  * @extends Roo.bootstrap.Input
13100  * Bootstrap DateField class
13101  * @cfg {Number} weekStart default 0
13102  * @cfg {Number} weekStart default 0
13103  * @cfg {Number} viewMode default empty, (months|years)
13104  * @cfg {Number} minViewMode default empty, (months|years)
13105  * @cfg {Number} startDate default -Infinity
13106  * @cfg {Number} endDate default Infinity
13107  * @cfg {Boolean} todayHighlight default false
13108  * @cfg {Boolean} todayBtn default false
13109  * @cfg {Boolean} calendarWeeks default false
13110  * @cfg {Object} daysOfWeekDisabled default empty
13111  * 
13112  * @cfg {Boolean} keyboardNavigation default true
13113  * @cfg {String} language default en
13114  * 
13115  * @constructor
13116  * Create a new DateField
13117  * @param {Object} config The config object
13118  */
13119
13120 Roo.bootstrap.DateField = function(config){
13121     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
13122      this.addEvents({
13123             /**
13124              * @event show
13125              * Fires when this field show.
13126              * @param {Roo.bootstrap.DateField} this
13127              * @param {Mixed} date The date value
13128              */
13129             show : true,
13130             /**
13131              * @event show
13132              * Fires when this field hide.
13133              * @param {Roo.bootstrap.DateField} this
13134              * @param {Mixed} date The date value
13135              */
13136             hide : true,
13137             /**
13138              * @event select
13139              * Fires when select a date.
13140              * @param {Roo.bootstrap.DateField} this
13141              * @param {Mixed} date The date value
13142              */
13143             select : true
13144         });
13145 };
13146
13147 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
13148     
13149     /**
13150      * @cfg {String} format
13151      * The default date format string which can be overriden for localization support.  The format must be
13152      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
13153      */
13154     format : "m/d/y",
13155     /**
13156      * @cfg {String} altFormats
13157      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
13158      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
13159      */
13160     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
13161     
13162     weekStart : 0,
13163     
13164     viewMode : '',
13165     
13166     minViewMode : '',
13167     
13168     todayHighlight : false,
13169     
13170     todayBtn: false,
13171     
13172     language: 'en',
13173     
13174     keyboardNavigation: true,
13175     
13176     calendarWeeks: false,
13177     
13178     startDate: -Infinity,
13179     
13180     endDate: Infinity,
13181     
13182     daysOfWeekDisabled: [],
13183     
13184     _events: [],
13185     
13186     UTCDate: function()
13187     {
13188         return new Date(Date.UTC.apply(Date, arguments));
13189     },
13190     
13191     UTCToday: function()
13192     {
13193         var today = new Date();
13194         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
13195     },
13196     
13197     getDate: function() {
13198             var d = this.getUTCDate();
13199             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
13200     },
13201     
13202     getUTCDate: function() {
13203             return this.date;
13204     },
13205     
13206     setDate: function(d) {
13207             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
13208     },
13209     
13210     setUTCDate: function(d) {
13211             this.date = d;
13212             this.setValue(this.formatDate(this.date));
13213     },
13214         
13215     onRender: function(ct, position)
13216     {
13217         
13218         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
13219         
13220         this.language = this.language || 'en';
13221         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
13222         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
13223         
13224         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
13225         this.format = this.format || 'm/d/y';
13226         this.isInline = false;
13227         this.isInput = true;
13228         this.component = this.el.select('.add-on', true).first() || false;
13229         this.component = (this.component && this.component.length === 0) ? false : this.component;
13230         this.hasInput = this.component && this.inputEL().length;
13231         
13232         if (typeof(this.minViewMode === 'string')) {
13233             switch (this.minViewMode) {
13234                 case 'months':
13235                     this.minViewMode = 1;
13236                     break;
13237                 case 'years':
13238                     this.minViewMode = 2;
13239                     break;
13240                 default:
13241                     this.minViewMode = 0;
13242                     break;
13243             }
13244         }
13245         
13246         if (typeof(this.viewMode === 'string')) {
13247             switch (this.viewMode) {
13248                 case 'months':
13249                     this.viewMode = 1;
13250                     break;
13251                 case 'years':
13252                     this.viewMode = 2;
13253                     break;
13254                 default:
13255                     this.viewMode = 0;
13256                     break;
13257             }
13258         }
13259                 
13260         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
13261         
13262         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13263         
13264         this.picker().on('mousedown', this.onMousedown, this);
13265         this.picker().on('click', this.onClick, this);
13266         
13267         this.picker().addClass('datepicker-dropdown');
13268         
13269         this.startViewMode = this.viewMode;
13270         
13271         
13272         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
13273             if(!this.calendarWeeks){
13274                 v.remove();
13275                 return;
13276             };
13277             
13278             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
13279             v.attr('colspan', function(i, val){
13280                 return parseInt(val) + 1;
13281             });
13282         })
13283                         
13284         
13285         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
13286         
13287         this.setStartDate(this.startDate);
13288         this.setEndDate(this.endDate);
13289         
13290         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
13291         
13292         this.fillDow();
13293         this.fillMonths();
13294         this.update();
13295         this.showMode();
13296         
13297         if(this.isInline) {
13298             this.show();
13299         }
13300     },
13301     
13302     picker : function()
13303     {
13304         return this.el.select('.datepicker', true).first();
13305     },
13306     
13307     fillDow: function()
13308     {
13309         var dowCnt = this.weekStart;
13310         
13311         var dow = {
13312             tag: 'tr',
13313             cn: [
13314                 
13315             ]
13316         };
13317         
13318         if(this.calendarWeeks){
13319             dow.cn.push({
13320                 tag: 'th',
13321                 cls: 'cw',
13322                 html: '&nbsp;'
13323             })
13324         }
13325         
13326         while (dowCnt < this.weekStart + 7) {
13327             dow.cn.push({
13328                 tag: 'th',
13329                 cls: 'dow',
13330                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
13331             });
13332         }
13333         
13334         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
13335     },
13336     
13337     fillMonths: function()
13338     {    
13339         var i = 0
13340         var months = this.picker().select('>.datepicker-months td', true).first();
13341         
13342         months.dom.innerHTML = '';
13343         
13344         while (i < 12) {
13345             var month = {
13346                 tag: 'span',
13347                 cls: 'month',
13348                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
13349             }
13350             
13351             months.createChild(month);
13352         }
13353         
13354     },
13355     
13356     update: function(){
13357         
13358         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
13359         
13360         if (this.date < this.startDate) {
13361             this.viewDate = new Date(this.startDate);
13362         } else if (this.date > this.endDate) {
13363             this.viewDate = new Date(this.endDate);
13364         } else {
13365             this.viewDate = new Date(this.date);
13366         }
13367         
13368         this.fill();
13369     },
13370     
13371     fill: function() {
13372         var d = new Date(this.viewDate),
13373                 year = d.getUTCFullYear(),
13374                 month = d.getUTCMonth(),
13375                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
13376                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
13377                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
13378                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
13379                 currentDate = this.date && this.date.valueOf(),
13380                 today = this.UTCToday();
13381         
13382         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
13383         
13384 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
13385         
13386 //        this.picker.select('>tfoot th.today').
13387 //                                              .text(dates[this.language].today)
13388 //                                              .toggle(this.todayBtn !== false);
13389     
13390         this.updateNavArrows();
13391         this.fillMonths();
13392                                                 
13393         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
13394         
13395         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
13396          
13397         prevMonth.setUTCDate(day);
13398         
13399         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
13400         
13401         var nextMonth = new Date(prevMonth);
13402         
13403         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
13404         
13405         nextMonth = nextMonth.valueOf();
13406         
13407         var fillMonths = false;
13408         
13409         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
13410         
13411         while(prevMonth.valueOf() < nextMonth) {
13412             var clsName = '';
13413             
13414             if (prevMonth.getUTCDay() === this.weekStart) {
13415                 if(fillMonths){
13416                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
13417                 }
13418                     
13419                 fillMonths = {
13420                     tag: 'tr',
13421                     cn: []
13422                 };
13423                 
13424                 if(this.calendarWeeks){
13425                     // ISO 8601: First week contains first thursday.
13426                     // ISO also states week starts on Monday, but we can be more abstract here.
13427                     var
13428                     // Start of current week: based on weekstart/current date
13429                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
13430                     // Thursday of this week
13431                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
13432                     // First Thursday of year, year from thursday
13433                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
13434                     // Calendar week: ms between thursdays, div ms per day, div 7 days
13435                     calWeek =  (th - yth) / 864e5 / 7 + 1;
13436                     
13437                     fillMonths.cn.push({
13438                         tag: 'td',
13439                         cls: 'cw',
13440                         html: calWeek
13441                     });
13442                 }
13443             }
13444             
13445             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
13446                 clsName += ' old';
13447             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
13448                 clsName += ' new';
13449             }
13450             if (this.todayHighlight &&
13451                 prevMonth.getUTCFullYear() == today.getFullYear() &&
13452                 prevMonth.getUTCMonth() == today.getMonth() &&
13453                 prevMonth.getUTCDate() == today.getDate()) {
13454                 clsName += ' today';
13455             }
13456             
13457             if (currentDate && prevMonth.valueOf() === currentDate) {
13458                 clsName += ' active';
13459             }
13460             
13461             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
13462                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
13463                     clsName += ' disabled';
13464             }
13465             
13466             fillMonths.cn.push({
13467                 tag: 'td',
13468                 cls: 'day ' + clsName,
13469                 html: prevMonth.getDate()
13470             })
13471             
13472             prevMonth.setDate(prevMonth.getDate()+1);
13473         }
13474           
13475         var currentYear = this.date && this.date.getUTCFullYear();
13476         var currentMonth = this.date && this.date.getUTCMonth();
13477         
13478         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
13479         
13480         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
13481             v.removeClass('active');
13482             
13483             if(currentYear === year && k === currentMonth){
13484                 v.addClass('active');
13485             }
13486             
13487             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
13488                 v.addClass('disabled');
13489             }
13490             
13491         });
13492         
13493         
13494         year = parseInt(year/10, 10) * 10;
13495         
13496         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
13497         
13498         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
13499         
13500         year -= 1;
13501         for (var i = -1; i < 11; i++) {
13502             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
13503                 tag: 'span',
13504                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
13505                 html: year
13506             })
13507             
13508             year += 1;
13509         }
13510     },
13511     
13512     showMode: function(dir) {
13513         if (dir) {
13514             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
13515         }
13516         Roo.each(this.picker().select('>div',true).elements, function(v){
13517             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13518             v.hide();
13519         });
13520         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
13521     },
13522     
13523     place: function()
13524     {
13525         if(this.isInline) return;
13526         
13527         this.picker().removeClass(['bottom', 'top']);
13528         
13529         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13530             /*
13531              * place to the top of element!
13532              *
13533              */
13534             
13535             this.picker().addClass('top');
13536             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13537             
13538             return;
13539         }
13540         
13541         this.picker().addClass('bottom');
13542         
13543         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13544     },
13545     
13546     parseDate : function(value){
13547         if(!value || value instanceof Date){
13548             return value;
13549         }
13550         var v = Date.parseDate(value, this.format);
13551         if (!v && this.useIso) {
13552             v = Date.parseDate(value, 'Y-m-d');
13553         }
13554         if(!v && this.altFormats){
13555             if(!this.altFormatsArray){
13556                 this.altFormatsArray = this.altFormats.split("|");
13557             }
13558             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
13559                 v = Date.parseDate(value, this.altFormatsArray[i]);
13560             }
13561         }
13562         return v;
13563     },
13564     
13565     formatDate : function(date, fmt){
13566         return (!date || !(date instanceof Date)) ?
13567         date : date.dateFormat(fmt || this.format);
13568     },
13569     
13570     onFocus : function()
13571     {
13572         Roo.bootstrap.DateField.superclass.onFocus.call(this);
13573         this.show();
13574     },
13575     
13576     onBlur : function()
13577     {
13578         Roo.bootstrap.DateField.superclass.onBlur.call(this);
13579         this.hide();
13580     },
13581     
13582     show : function()
13583     {
13584         this.picker().show();
13585         this.update();
13586         this.place();
13587         
13588         this.fireEvent('show', this, this.date);
13589     },
13590     
13591     hide : function()
13592     {
13593         if(this.isInline) return;
13594         this.picker().hide();
13595         this.viewMode = this.startViewMode;
13596         this.showMode();
13597         
13598         this.fireEvent('hide', this, this.date);
13599         
13600     },
13601     
13602     onMousedown: function(e){
13603         e.stopPropagation();
13604         e.preventDefault();
13605     },
13606     
13607     keyup: function(e){
13608         Roo.bootstrap.DateField.superclass.keyup.call(this);
13609         this.update();
13610         
13611     },
13612
13613     setValue: function(v){
13614         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
13615         
13616         this.fireEvent('select', this, this.date);
13617         
13618     },
13619     
13620     fireKey: function(e){
13621         if (!this.picker().isVisible()){
13622             if (e.keyCode == 27) // allow escape to hide and re-show picker
13623                 this.show();
13624             return;
13625         }
13626         var dateChanged = false,
13627         dir, day, month,
13628         newDate, newViewDate;
13629         switch(e.keyCode){
13630             case 27: // escape
13631                 this.hide();
13632                 e.preventDefault();
13633                 break;
13634             case 37: // left
13635             case 39: // right
13636                 if (!this.keyboardNavigation) break;
13637                 dir = e.keyCode == 37 ? -1 : 1;
13638                 
13639                 if (e.ctrlKey){
13640                     newDate = this.moveYear(this.date, dir);
13641                     newViewDate = this.moveYear(this.viewDate, dir);
13642                 } else if (e.shiftKey){
13643                     newDate = this.moveMonth(this.date, dir);
13644                     newViewDate = this.moveMonth(this.viewDate, dir);
13645                 } else {
13646                     newDate = new Date(this.date);
13647                     newDate.setUTCDate(this.date.getUTCDate() + dir);
13648                     newViewDate = new Date(this.viewDate);
13649                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
13650                 }
13651                 if (this.dateWithinRange(newDate)){
13652                     this.date = newDate;
13653                     this.viewDate = newViewDate;
13654                     this.setValue(this.formatDate(this.date));
13655                     this.update();
13656                     e.preventDefault();
13657                     dateChanged = true;
13658                 }
13659                 break;
13660             case 38: // up
13661             case 40: // down
13662                 if (!this.keyboardNavigation) break;
13663                 dir = e.keyCode == 38 ? -1 : 1;
13664                 if (e.ctrlKey){
13665                     newDate = this.moveYear(this.date, dir);
13666                     newViewDate = this.moveYear(this.viewDate, dir);
13667                 } else if (e.shiftKey){
13668                     newDate = this.moveMonth(this.date, dir);
13669                     newViewDate = this.moveMonth(this.viewDate, dir);
13670                 } else {
13671                     newDate = new Date(this.date);
13672                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
13673                     newViewDate = new Date(this.viewDate);
13674                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
13675                 }
13676                 if (this.dateWithinRange(newDate)){
13677                     this.date = newDate;
13678                     this.viewDate = newViewDate;
13679                     this.setValue(this.formatDate(this.date));
13680                     this.update();
13681                     e.preventDefault();
13682                     dateChanged = true;
13683                 }
13684                 break;
13685             case 13: // enter
13686                 this.setValue(this.formatDate(this.date));
13687                 this.hide();
13688                 e.preventDefault();
13689                 break;
13690             case 9: // tab
13691                 this.setValue(this.formatDate(this.date));
13692                 this.hide();
13693                 break;
13694         }
13695     },
13696     
13697     
13698     onClick: function(e) {
13699         e.stopPropagation();
13700         e.preventDefault();
13701         
13702         var target = e.getTarget();
13703         
13704         if(target.nodeName.toLowerCase() === 'i'){
13705             target = Roo.get(target).dom.parentNode;
13706         }
13707         
13708         var nodeName = target.nodeName;
13709         var className = target.className;
13710         var html = target.innerHTML;
13711         
13712         switch(nodeName.toLowerCase()) {
13713             case 'th':
13714                 switch(className) {
13715                     case 'switch':
13716                         this.showMode(1);
13717                         break;
13718                     case 'prev':
13719                     case 'next':
13720                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
13721                         switch(this.viewMode){
13722                                 case 0:
13723                                         this.viewDate = this.moveMonth(this.viewDate, dir);
13724                                         break;
13725                                 case 1:
13726                                 case 2:
13727                                         this.viewDate = this.moveYear(this.viewDate, dir);
13728                                         break;
13729                         }
13730                         this.fill();
13731                         break;
13732                     case 'today':
13733                         var date = new Date();
13734                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
13735                         this.fill()
13736                         this.setValue(this.formatDate(this.date));
13737                         this.hide();
13738                         break;
13739                 }
13740                 break;
13741             case 'span':
13742                 if (className.indexOf('disabled') === -1) {
13743                     this.viewDate.setUTCDate(1);
13744                     if (className.indexOf('month') !== -1) {
13745                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
13746                     } else {
13747                         var year = parseInt(html, 10) || 0;
13748                         this.viewDate.setUTCFullYear(year);
13749                         
13750                     }
13751                     this.showMode(-1);
13752                     this.fill();
13753                 }
13754                 break;
13755                 
13756             case 'td':
13757                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
13758                     var day = parseInt(html, 10) || 1;
13759                     var year = this.viewDate.getUTCFullYear(),
13760                         month = this.viewDate.getUTCMonth();
13761
13762                     if (className.indexOf('old') !== -1) {
13763                         if(month === 0 ){
13764                             month = 11;
13765                             year -= 1;
13766                         }else{
13767                             month -= 1;
13768                         }
13769                     } else if (className.indexOf('new') !== -1) {
13770                         if (month == 11) {
13771                             month = 0;
13772                             year += 1;
13773                         } else {
13774                             month += 1;
13775                         }
13776                     }
13777                     this.date = this.UTCDate(year, month, day,0,0,0,0);
13778                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
13779                     this.fill();
13780                     this.setValue(this.formatDate(this.date));
13781                     this.hide();
13782                 }
13783                 break;
13784         }
13785     },
13786     
13787     setStartDate: function(startDate){
13788         this.startDate = startDate || -Infinity;
13789         if (this.startDate !== -Infinity) {
13790             this.startDate = this.parseDate(this.startDate);
13791         }
13792         this.update();
13793         this.updateNavArrows();
13794     },
13795
13796     setEndDate: function(endDate){
13797         this.endDate = endDate || Infinity;
13798         if (this.endDate !== Infinity) {
13799             this.endDate = this.parseDate(this.endDate);
13800         }
13801         this.update();
13802         this.updateNavArrows();
13803     },
13804     
13805     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
13806         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
13807         if (typeof(this.daysOfWeekDisabled) !== 'object') {
13808             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
13809         }
13810         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
13811             return parseInt(d, 10);
13812         });
13813         this.update();
13814         this.updateNavArrows();
13815     },
13816     
13817     updateNavArrows: function() {
13818         var d = new Date(this.viewDate),
13819         year = d.getUTCFullYear(),
13820         month = d.getUTCMonth();
13821         
13822         Roo.each(this.picker().select('.prev', true).elements, function(v){
13823             v.show();
13824             switch (this.viewMode) {
13825                 case 0:
13826
13827                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
13828                         v.hide();
13829                     }
13830                     break;
13831                 case 1:
13832                 case 2:
13833                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
13834                         v.hide();
13835                     }
13836                     break;
13837             }
13838         });
13839         
13840         Roo.each(this.picker().select('.next', true).elements, function(v){
13841             v.show();
13842             switch (this.viewMode) {
13843                 case 0:
13844
13845                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
13846                         v.hide();
13847                     }
13848                     break;
13849                 case 1:
13850                 case 2:
13851                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
13852                         v.hide();
13853                     }
13854                     break;
13855             }
13856         })
13857     },
13858     
13859     moveMonth: function(date, dir){
13860         if (!dir) return date;
13861         var new_date = new Date(date.valueOf()),
13862         day = new_date.getUTCDate(),
13863         month = new_date.getUTCMonth(),
13864         mag = Math.abs(dir),
13865         new_month, test;
13866         dir = dir > 0 ? 1 : -1;
13867         if (mag == 1){
13868             test = dir == -1
13869             // If going back one month, make sure month is not current month
13870             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
13871             ? function(){
13872                 return new_date.getUTCMonth() == month;
13873             }
13874             // If going forward one month, make sure month is as expected
13875             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
13876             : function(){
13877                 return new_date.getUTCMonth() != new_month;
13878             };
13879             new_month = month + dir;
13880             new_date.setUTCMonth(new_month);
13881             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
13882             if (new_month < 0 || new_month > 11)
13883                 new_month = (new_month + 12) % 12;
13884         } else {
13885             // For magnitudes >1, move one month at a time...
13886             for (var i=0; i<mag; i++)
13887                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
13888                 new_date = this.moveMonth(new_date, dir);
13889             // ...then reset the day, keeping it in the new month
13890             new_month = new_date.getUTCMonth();
13891             new_date.setUTCDate(day);
13892             test = function(){
13893                 return new_month != new_date.getUTCMonth();
13894             };
13895         }
13896         // Common date-resetting loop -- if date is beyond end of month, make it
13897         // end of month
13898         while (test()){
13899             new_date.setUTCDate(--day);
13900             new_date.setUTCMonth(new_month);
13901         }
13902         return new_date;
13903     },
13904
13905     moveYear: function(date, dir){
13906         return this.moveMonth(date, dir*12);
13907     },
13908
13909     dateWithinRange: function(date){
13910         return date >= this.startDate && date <= this.endDate;
13911     },
13912
13913     
13914     remove: function() {
13915         this.picker().remove();
13916     }
13917    
13918 });
13919
13920 Roo.apply(Roo.bootstrap.DateField,  {
13921     
13922     head : {
13923         tag: 'thead',
13924         cn: [
13925         {
13926             tag: 'tr',
13927             cn: [
13928             {
13929                 tag: 'th',
13930                 cls: 'prev',
13931                 html: '<i class="fa fa-arrow-left"/>'
13932             },
13933             {
13934                 tag: 'th',
13935                 cls: 'switch',
13936                 colspan: '5'
13937             },
13938             {
13939                 tag: 'th',
13940                 cls: 'next',
13941                 html: '<i class="fa fa-arrow-right"/>'
13942             }
13943
13944             ]
13945         }
13946         ]
13947     },
13948     
13949     content : {
13950         tag: 'tbody',
13951         cn: [
13952         {
13953             tag: 'tr',
13954             cn: [
13955             {
13956                 tag: 'td',
13957                 colspan: '7'
13958             }
13959             ]
13960         }
13961         ]
13962     },
13963     
13964     footer : {
13965         tag: 'tfoot',
13966         cn: [
13967         {
13968             tag: 'tr',
13969             cn: [
13970             {
13971                 tag: 'th',
13972                 colspan: '7',
13973                 cls: 'today'
13974             }
13975                     
13976             ]
13977         }
13978         ]
13979     },
13980     
13981     dates:{
13982         en: {
13983             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
13984             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
13985             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
13986             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
13987             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
13988             today: "Today"
13989         }
13990     },
13991     
13992     modes: [
13993     {
13994         clsName: 'days',
13995         navFnc: 'Month',
13996         navStep: 1
13997     },
13998     {
13999         clsName: 'months',
14000         navFnc: 'FullYear',
14001         navStep: 1
14002     },
14003     {
14004         clsName: 'years',
14005         navFnc: 'FullYear',
14006         navStep: 10
14007     }]
14008 });
14009
14010 Roo.apply(Roo.bootstrap.DateField,  {
14011   
14012     template : {
14013         tag: 'div',
14014         cls: 'datepicker dropdown-menu',
14015         cn: [
14016         {
14017             tag: 'div',
14018             cls: 'datepicker-days',
14019             cn: [
14020             {
14021                 tag: 'table',
14022                 cls: 'table-condensed',
14023                 cn:[
14024                 Roo.bootstrap.DateField.head,
14025                 {
14026                     tag: 'tbody'
14027                 },
14028                 Roo.bootstrap.DateField.footer
14029                 ]
14030             }
14031             ]
14032         },
14033         {
14034             tag: 'div',
14035             cls: 'datepicker-months',
14036             cn: [
14037             {
14038                 tag: 'table',
14039                 cls: 'table-condensed',
14040                 cn:[
14041                 Roo.bootstrap.DateField.head,
14042                 Roo.bootstrap.DateField.content,
14043                 Roo.bootstrap.DateField.footer
14044                 ]
14045             }
14046             ]
14047         },
14048         {
14049             tag: 'div',
14050             cls: 'datepicker-years',
14051             cn: [
14052             {
14053                 tag: 'table',
14054                 cls: 'table-condensed',
14055                 cn:[
14056                 Roo.bootstrap.DateField.head,
14057                 Roo.bootstrap.DateField.content,
14058                 Roo.bootstrap.DateField.footer
14059                 ]
14060             }
14061             ]
14062         }
14063         ]
14064     }
14065 });
14066
14067  
14068
14069  /*
14070  * - LGPL
14071  *
14072  * TimeField
14073  * 
14074  */
14075
14076 /**
14077  * @class Roo.bootstrap.TimeField
14078  * @extends Roo.bootstrap.Input
14079  * Bootstrap DateField class
14080  * 
14081  * 
14082  * @constructor
14083  * Create a new TimeField
14084  * @param {Object} config The config object
14085  */
14086
14087 Roo.bootstrap.TimeField = function(config){
14088     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
14089     this.addEvents({
14090             /**
14091              * @event show
14092              * Fires when this field show.
14093              * @param {Roo.bootstrap.DateField} this
14094              * @param {Mixed} date The date value
14095              */
14096             show : true,
14097             /**
14098              * @event show
14099              * Fires when this field hide.
14100              * @param {Roo.bootstrap.DateField} this
14101              * @param {Mixed} date The date value
14102              */
14103             hide : true,
14104             /**
14105              * @event select
14106              * Fires when select a date.
14107              * @param {Roo.bootstrap.DateField} this
14108              * @param {Mixed} date The date value
14109              */
14110             select : true
14111         });
14112 };
14113
14114 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
14115     
14116     /**
14117      * @cfg {String} format
14118      * The default time format string which can be overriden for localization support.  The format must be
14119      * valid according to {@link Date#parseDate} (defaults to 'H:i').
14120      */
14121     format : "H:i",
14122        
14123     onRender: function(ct, position)
14124     {
14125         
14126         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
14127                 
14128         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
14129         
14130         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14131         
14132         this.pop = this.picker().select('>.datepicker-time',true).first();
14133         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
14134         
14135         this.picker().on('mousedown', this.onMousedown, this);
14136         this.picker().on('click', this.onClick, this);
14137         
14138         this.picker().addClass('datepicker-dropdown');
14139     
14140         this.fillTime();
14141         this.update();
14142             
14143         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
14144         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
14145         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
14146         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
14147         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
14148         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
14149
14150     },
14151     
14152     fireKey: function(e){
14153         if (!this.picker().isVisible()){
14154             if (e.keyCode == 27) // allow escape to hide and re-show picker
14155                 this.show();
14156             return;
14157         }
14158
14159         e.preventDefault();
14160         
14161         switch(e.keyCode){
14162             case 27: // escape
14163                 this.hide();
14164                 break;
14165             case 37: // left
14166             case 39: // right
14167                 this.onTogglePeriod();
14168                 break;
14169             case 38: // up
14170                 this.onIncrementMinutes();
14171                 break;
14172             case 40: // down
14173                 this.onDecrementMinutes();
14174                 break;
14175             case 13: // enter
14176             case 9: // tab
14177                 this.setTime();
14178                 break;
14179         }
14180     },
14181     
14182     onClick: function(e) {
14183         e.stopPropagation();
14184         e.preventDefault();
14185     },
14186     
14187     picker : function()
14188     {
14189         return this.el.select('.datepicker', true).first();
14190     },
14191     
14192     fillTime: function()
14193     {    
14194         var time = this.pop.select('tbody', true).first();
14195         
14196         time.dom.innerHTML = '';
14197         
14198         time.createChild({
14199             tag: 'tr',
14200             cn: [
14201                 {
14202                     tag: 'td',
14203                     cn: [
14204                         {
14205                             tag: 'a',
14206                             href: '#',
14207                             cls: 'btn',
14208                             cn: [
14209                                 {
14210                                     tag: 'span',
14211                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
14212                                 }
14213                             ]
14214                         } 
14215                     ]
14216                 },
14217                 {
14218                     tag: 'td',
14219                     cls: 'separator'
14220                 },
14221                 {
14222                     tag: 'td',
14223                     cn: [
14224                         {
14225                             tag: 'a',
14226                             href: '#',
14227                             cls: 'btn',
14228                             cn: [
14229                                 {
14230                                     tag: 'span',
14231                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
14232                                 }
14233                             ]
14234                         }
14235                     ]
14236                 },
14237                 {
14238                     tag: 'td',
14239                     cls: 'separator'
14240                 }
14241             ]
14242         });
14243         
14244         time.createChild({
14245             tag: 'tr',
14246             cn: [
14247                 {
14248                     tag: 'td',
14249                     cn: [
14250                         {
14251                             tag: 'span',
14252                             cls: 'timepicker-hour',
14253                             html: '00'
14254                         }  
14255                     ]
14256                 },
14257                 {
14258                     tag: 'td',
14259                     cls: 'separator',
14260                     html: ':'
14261                 },
14262                 {
14263                     tag: 'td',
14264                     cn: [
14265                         {
14266                             tag: 'span',
14267                             cls: 'timepicker-minute',
14268                             html: '00'
14269                         }  
14270                     ]
14271                 },
14272                 {
14273                     tag: 'td',
14274                     cls: 'separator'
14275                 },
14276                 {
14277                     tag: 'td',
14278                     cn: [
14279                         {
14280                             tag: 'button',
14281                             type: 'button',
14282                             cls: 'btn btn-primary period',
14283                             html: 'AM'
14284                             
14285                         }
14286                     ]
14287                 }
14288             ]
14289         });
14290         
14291         time.createChild({
14292             tag: 'tr',
14293             cn: [
14294                 {
14295                     tag: 'td',
14296                     cn: [
14297                         {
14298                             tag: 'a',
14299                             href: '#',
14300                             cls: 'btn',
14301                             cn: [
14302                                 {
14303                                     tag: 'span',
14304                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
14305                                 }
14306                             ]
14307                         }
14308                     ]
14309                 },
14310                 {
14311                     tag: 'td',
14312                     cls: 'separator'
14313                 },
14314                 {
14315                     tag: 'td',
14316                     cn: [
14317                         {
14318                             tag: 'a',
14319                             href: '#',
14320                             cls: 'btn',
14321                             cn: [
14322                                 {
14323                                     tag: 'span',
14324                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
14325                                 }
14326                             ]
14327                         }
14328                     ]
14329                 },
14330                 {
14331                     tag: 'td',
14332                     cls: 'separator'
14333                 }
14334             ]
14335         });
14336         
14337     },
14338     
14339     update: function()
14340     {
14341         
14342         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
14343         
14344         this.fill();
14345     },
14346     
14347     fill: function() 
14348     {
14349         var hours = this.time.getHours();
14350         var minutes = this.time.getMinutes();
14351         var period = 'AM';
14352         
14353         if(hours > 11){
14354             period = 'PM';
14355         }
14356         
14357         if(hours == 0){
14358             hours = 12;
14359         }
14360         
14361         
14362         if(hours > 12){
14363             hours = hours - 12;
14364         }
14365         
14366         if(hours < 10){
14367             hours = '0' + hours;
14368         }
14369         
14370         if(minutes < 10){
14371             minutes = '0' + minutes;
14372         }
14373         
14374         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
14375         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
14376         this.pop.select('button', true).first().dom.innerHTML = period;
14377         
14378     },
14379     
14380     place: function()
14381     {   
14382         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
14383         
14384         var cls = ['bottom'];
14385         
14386         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
14387             cls.pop();
14388             cls.push('top');
14389         }
14390         
14391         cls.push('right');
14392         
14393         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
14394             cls.pop();
14395             cls.push('left');
14396         }
14397         
14398         this.picker().addClass(cls.join('-'));
14399         
14400         var _this = this;
14401         
14402         Roo.each(cls, function(c){
14403             if(c == 'bottom'){
14404                 _this.picker().setTop(_this.inputEl().getHeight());
14405                 return;
14406             }
14407             if(c == 'top'){
14408                 _this.picker().setTop(0 - _this.picker().getHeight());
14409                 return;
14410             }
14411             
14412             if(c == 'left'){
14413                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
14414                 return;
14415             }
14416             if(c == 'right'){
14417                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
14418                 return;
14419             }
14420         });
14421         
14422     },
14423   
14424     onFocus : function()
14425     {
14426         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
14427         this.show();
14428     },
14429     
14430     onBlur : function()
14431     {
14432         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
14433         this.hide();
14434     },
14435     
14436     show : function()
14437     {
14438         this.picker().show();
14439         this.pop.show();
14440         this.update();
14441         this.place();
14442         
14443         this.fireEvent('show', this, this.date);
14444     },
14445     
14446     hide : function()
14447     {
14448         this.picker().hide();
14449         this.pop.hide();
14450         
14451         this.fireEvent('hide', this, this.date);
14452     },
14453     
14454     setTime : function()
14455     {
14456         this.hide();
14457         this.setValue(this.time.format(this.format));
14458         
14459         this.fireEvent('select', this, this.date);
14460         
14461         
14462     },
14463     
14464     onMousedown: function(e){
14465         e.stopPropagation();
14466         e.preventDefault();
14467     },
14468     
14469     onIncrementHours: function()
14470     {
14471         Roo.log('onIncrementHours');
14472         this.time = this.time.add(Date.HOUR, 1);
14473         this.update();
14474         
14475     },
14476     
14477     onDecrementHours: function()
14478     {
14479         Roo.log('onDecrementHours');
14480         this.time = this.time.add(Date.HOUR, -1);
14481         this.update();
14482     },
14483     
14484     onIncrementMinutes: function()
14485     {
14486         Roo.log('onIncrementMinutes');
14487         this.time = this.time.add(Date.MINUTE, 1);
14488         this.update();
14489     },
14490     
14491     onDecrementMinutes: function()
14492     {
14493         Roo.log('onDecrementMinutes');
14494         this.time = this.time.add(Date.MINUTE, -1);
14495         this.update();
14496     },
14497     
14498     onTogglePeriod: function()
14499     {
14500         Roo.log('onTogglePeriod');
14501         this.time = this.time.add(Date.HOUR, 12);
14502         this.update();
14503     }
14504     
14505    
14506 });
14507
14508 Roo.apply(Roo.bootstrap.TimeField,  {
14509     
14510     content : {
14511         tag: 'tbody',
14512         cn: [
14513             {
14514                 tag: 'tr',
14515                 cn: [
14516                 {
14517                     tag: 'td',
14518                     colspan: '7'
14519                 }
14520                 ]
14521             }
14522         ]
14523     },
14524     
14525     footer : {
14526         tag: 'tfoot',
14527         cn: [
14528             {
14529                 tag: 'tr',
14530                 cn: [
14531                 {
14532                     tag: 'th',
14533                     colspan: '7',
14534                     cls: '',
14535                     cn: [
14536                         {
14537                             tag: 'button',
14538                             cls: 'btn btn-info ok',
14539                             html: 'OK'
14540                         }
14541                     ]
14542                 }
14543
14544                 ]
14545             }
14546         ]
14547     }
14548 });
14549
14550 Roo.apply(Roo.bootstrap.TimeField,  {
14551   
14552     template : {
14553         tag: 'div',
14554         cls: 'datepicker dropdown-menu',
14555         cn: [
14556             {
14557                 tag: 'div',
14558                 cls: 'datepicker-time',
14559                 cn: [
14560                 {
14561                     tag: 'table',
14562                     cls: 'table-condensed',
14563                     cn:[
14564                     Roo.bootstrap.TimeField.content,
14565                     Roo.bootstrap.TimeField.footer
14566                     ]
14567                 }
14568                 ]
14569             }
14570         ]
14571     }
14572 });
14573
14574  
14575
14576  /*
14577  * - LGPL
14578  *
14579  * CheckBox
14580  * 
14581  */
14582
14583 /**
14584  * @class Roo.bootstrap.CheckBox
14585  * @extends Roo.bootstrap.Input
14586  * Bootstrap CheckBox class
14587  * 
14588  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
14589  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
14590  * @cfg {String} boxLabel The text that appears beside the checkbox
14591  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
14592  * @cfg {Boolean} checked initnal the element
14593  * 
14594  * 
14595  * @constructor
14596  * Create a new CheckBox
14597  * @param {Object} config The config object
14598  */
14599
14600 Roo.bootstrap.CheckBox = function(config){
14601     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
14602    
14603         this.addEvents({
14604             /**
14605             * @event check
14606             * Fires when the element is checked or unchecked.
14607             * @param {Roo.bootstrap.CheckBox} this This input
14608             * @param {Boolean} checked The new checked value
14609             */
14610            check : true
14611         });
14612 };
14613
14614 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
14615     
14616     inputType: 'checkbox',
14617     inputValue: 1,
14618     valueOff: 0,
14619     boxLabel: false,
14620     checked: false,
14621     weight : false,
14622     
14623     getAutoCreate : function()
14624     {
14625         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
14626         
14627         var id = Roo.id();
14628         
14629         var cfg = {};
14630         
14631         cfg.cls = 'form-group checkbox' //input-group
14632         
14633         
14634         
14635         
14636         var input =  {
14637             tag: 'input',
14638             id : id,
14639             type : this.inputType,
14640             value : (!this.checked) ? this.valueOff : this.inputValue,
14641             cls : 'roo-checkbox', //'form-box',
14642             placeholder : this.placeholder || ''
14643             
14644         };
14645         
14646         if (this.weight) { // Validity check?
14647             cfg.cls += " checkbox-" + this.weight;
14648         }
14649         
14650         if (this.disabled) {
14651             input.disabled=true;
14652         }
14653         
14654         if(this.checked){
14655             input.checked = this.checked;
14656         }
14657         
14658         if (this.name) {
14659             input.name = this.name;
14660         }
14661         
14662         if (this.size) {
14663             input.cls += ' input-' + this.size;
14664         }
14665         
14666         var settings=this;
14667         ['xs','sm','md','lg'].map(function(size){
14668             if (settings[size]) {
14669                 cfg.cls += ' col-' + size + '-' + settings[size];
14670             }
14671         });
14672         
14673        
14674         
14675         var inputblock = input;
14676         
14677         
14678         
14679         
14680         if (this.before || this.after) {
14681             
14682             inputblock = {
14683                 cls : 'input-group',
14684                 cn :  [] 
14685             };
14686             if (this.before) {
14687                 inputblock.cn.push({
14688                     tag :'span',
14689                     cls : 'input-group-addon',
14690                     html : this.before
14691                 });
14692             }
14693             inputblock.cn.push(input);
14694             if (this.after) {
14695                 inputblock.cn.push({
14696                     tag :'span',
14697                     cls : 'input-group-addon',
14698                     html : this.after
14699                 });
14700             }
14701             
14702         };
14703         
14704         if (align ==='left' && this.fieldLabel.length) {
14705                 Roo.log("left and has label");
14706                 cfg.cn = [
14707                     
14708                     {
14709                         tag: 'label',
14710                         'for' :  id,
14711                         cls : 'control-label col-md-' + this.labelWidth,
14712                         html : this.fieldLabel
14713                         
14714                     },
14715                     {
14716                         cls : "col-md-" + (12 - this.labelWidth), 
14717                         cn: [
14718                             inputblock
14719                         ]
14720                     }
14721                     
14722                 ];
14723         } else if ( this.fieldLabel.length) {
14724                 Roo.log(" label");
14725                 cfg.cn = [
14726                    
14727                     {
14728                         tag: this.boxLabel ? 'span' : 'label',
14729                         'for': id,
14730                         cls: 'control-label box-input-label',
14731                         //cls : 'input-group-addon',
14732                         html : this.fieldLabel
14733                         
14734                     },
14735                     
14736                     inputblock
14737                     
14738                 ];
14739
14740         } else {
14741             
14742                 Roo.log(" no label && no align");
14743                 cfg.cn = [  inputblock ] ;
14744                 
14745                 
14746         };
14747          if(this.boxLabel){
14748             cfg.cn.push( {
14749                 tag: 'label',
14750                 'for': id,
14751                 cls: 'box-label',
14752                 html: this.boxLabel
14753                 
14754             });
14755         }
14756         
14757         
14758        
14759         return cfg;
14760         
14761     },
14762     
14763     /**
14764      * return the real input element.
14765      */
14766     inputEl: function ()
14767     {
14768         return this.el.select('input.roo-checkbox',true).first();
14769     },
14770     
14771     label: function()
14772     {
14773         return this.el.select('label.control-label',true).first();
14774     },
14775     
14776     initEvents : function()
14777     {
14778 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
14779         
14780         this.inputEl().on('click', this.onClick,  this);
14781         
14782     },
14783     
14784     onClick : function()
14785     {   
14786         this.setChecked(!this.checked);
14787     },
14788     
14789     setChecked : function(state,suppressEvent)
14790     {
14791         this.checked = state;
14792         
14793         this.inputEl().dom.checked = state;
14794         
14795         if(suppressEvent !== true){
14796             this.fireEvent('check', this, state);
14797         }
14798         
14799         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14800         
14801     },
14802     
14803     setValue : function(v,suppressEvent)
14804     {
14805         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
14806     }
14807     
14808 });
14809
14810  
14811 /*
14812  * - LGPL
14813  *
14814  * Radio
14815  * 
14816  */
14817
14818 /**
14819  * @class Roo.bootstrap.Radio
14820  * @extends Roo.bootstrap.CheckBox
14821  * Bootstrap Radio class
14822
14823  * @constructor
14824  * Create a new Radio
14825  * @param {Object} config The config object
14826  */
14827
14828 Roo.bootstrap.Radio = function(config){
14829     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
14830    
14831 };
14832
14833 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
14834     
14835     inputType: 'radio',
14836     inputValue: '',
14837     valueOff: '',
14838     
14839     getAutoCreate : function()
14840     {
14841         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
14842         
14843         var id = Roo.id();
14844         
14845         var cfg = {};
14846         
14847         cfg.cls = 'form-group radio' //input-group
14848         
14849         var input =  {
14850             tag: 'input',
14851             id : id,
14852             type : this.inputType,
14853             value : (!this.checked) ? this.valueOff : this.inputValue,
14854             cls : 'roo-radio',
14855             placeholder : this.placeholder || ''
14856             
14857         };
14858           if (this.weight) { // Validity check?
14859             cfg.cls += " radio-" + this.weight;
14860         }
14861         if (this.disabled) {
14862             input.disabled=true;
14863         }
14864         
14865         if(this.checked){
14866             input.checked = this.checked;
14867         }
14868         
14869         if (this.name) {
14870             input.name = this.name;
14871         }
14872         
14873         if (this.size) {
14874             input.cls += ' input-' + this.size;
14875         }
14876         
14877         var settings=this;
14878         ['xs','sm','md','lg'].map(function(size){
14879             if (settings[size]) {
14880                 cfg.cls += ' col-' + size + '-' + settings[size];
14881             }
14882         });
14883         
14884         var inputblock = input;
14885         
14886         if (this.before || this.after) {
14887             
14888             inputblock = {
14889                 cls : 'input-group',
14890                 cn :  [] 
14891             };
14892             if (this.before) {
14893                 inputblock.cn.push({
14894                     tag :'span',
14895                     cls : 'input-group-addon',
14896                     html : this.before
14897                 });
14898             }
14899             inputblock.cn.push(input);
14900             if (this.after) {
14901                 inputblock.cn.push({
14902                     tag :'span',
14903                     cls : 'input-group-addon',
14904                     html : this.after
14905                 });
14906             }
14907             
14908         };
14909         
14910         if (align ==='left' && this.fieldLabel.length) {
14911                 Roo.log("left and has label");
14912                 cfg.cn = [
14913                     
14914                     {
14915                         tag: 'label',
14916                         'for' :  id,
14917                         cls : 'control-label col-md-' + this.labelWidth,
14918                         html : this.fieldLabel
14919                         
14920                     },
14921                     {
14922                         cls : "col-md-" + (12 - this.labelWidth), 
14923                         cn: [
14924                             inputblock
14925                         ]
14926                     }
14927                     
14928                 ];
14929         } else if ( this.fieldLabel.length) {
14930                 Roo.log(" label");
14931                  cfg.cn = [
14932                    
14933                     {
14934                         tag: 'label',
14935                         'for': id,
14936                         cls: 'control-label box-input-label',
14937                         //cls : 'input-group-addon',
14938                         html : this.fieldLabel
14939                         
14940                     },
14941                     
14942                     inputblock
14943                     
14944                 ];
14945
14946         } else {
14947             
14948                    Roo.log(" no label && no align");
14949                 cfg.cn = [
14950                     
14951                         inputblock
14952                     
14953                 ];
14954                 
14955                 
14956         };
14957         
14958         if(this.boxLabel){
14959             cfg.cn.push({
14960                 tag: 'label',
14961                 'for': id,
14962                 cls: 'box-label',
14963                 html: this.boxLabel
14964             })
14965         }
14966         
14967         return cfg;
14968         
14969     },
14970     inputEl: function ()
14971     {
14972         return this.el.select('input.roo-radio',true).first();
14973     },
14974     onClick : function()
14975     {   
14976         this.setChecked(true);
14977     },
14978     
14979     setChecked : function(state,suppressEvent)
14980     {
14981         if(state){
14982             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14983                 v.dom.checked = false;
14984             });
14985         }
14986         
14987         this.checked = state;
14988         this.inputEl().dom.checked = state;
14989         
14990         if(suppressEvent !== true){
14991             this.fireEvent('check', this, state);
14992         }
14993         
14994         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14995         
14996     },
14997     
14998     getGroupValue : function()
14999     {
15000         var value = ''
15001         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
15002             if(v.dom.checked == true){
15003                 value = v.dom.value;
15004             }
15005         });
15006         
15007         return value;
15008     },
15009     
15010     /**
15011      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
15012      * @return {Mixed} value The field value
15013      */
15014     getValue : function(){
15015         return this.getGroupValue();
15016     }
15017     
15018 });
15019
15020  
15021 //<script type="text/javascript">
15022
15023 /*
15024  * Based  Ext JS Library 1.1.1
15025  * Copyright(c) 2006-2007, Ext JS, LLC.
15026  * LGPL
15027  *
15028  */
15029  
15030 /**
15031  * @class Roo.HtmlEditorCore
15032  * @extends Roo.Component
15033  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
15034  *
15035  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
15036  */
15037
15038 Roo.HtmlEditorCore = function(config){
15039     
15040     
15041     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
15042     this.addEvents({
15043         /**
15044          * @event initialize
15045          * Fires when the editor is fully initialized (including the iframe)
15046          * @param {Roo.HtmlEditorCore} this
15047          */
15048         initialize: true,
15049         /**
15050          * @event activate
15051          * Fires when the editor is first receives the focus. Any insertion must wait
15052          * until after this event.
15053          * @param {Roo.HtmlEditorCore} this
15054          */
15055         activate: true,
15056          /**
15057          * @event beforesync
15058          * Fires before the textarea is updated with content from the editor iframe. Return false
15059          * to cancel the sync.
15060          * @param {Roo.HtmlEditorCore} this
15061          * @param {String} html
15062          */
15063         beforesync: true,
15064          /**
15065          * @event beforepush
15066          * Fires before the iframe editor is updated with content from the textarea. Return false
15067          * to cancel the push.
15068          * @param {Roo.HtmlEditorCore} this
15069          * @param {String} html
15070          */
15071         beforepush: true,
15072          /**
15073          * @event sync
15074          * Fires when the textarea is updated with content from the editor iframe.
15075          * @param {Roo.HtmlEditorCore} this
15076          * @param {String} html
15077          */
15078         sync: true,
15079          /**
15080          * @event push
15081          * Fires when the iframe editor is updated with content from the textarea.
15082          * @param {Roo.HtmlEditorCore} this
15083          * @param {String} html
15084          */
15085         push: true,
15086         
15087         /**
15088          * @event editorevent
15089          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15090          * @param {Roo.HtmlEditorCore} this
15091          */
15092         editorevent: true
15093     });
15094      
15095 };
15096
15097
15098 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
15099
15100
15101      /**
15102      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
15103      */
15104     
15105     owner : false,
15106     
15107      /**
15108      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15109      *                        Roo.resizable.
15110      */
15111     resizable : false,
15112      /**
15113      * @cfg {Number} height (in pixels)
15114      */   
15115     height: 300,
15116    /**
15117      * @cfg {Number} width (in pixels)
15118      */   
15119     width: 500,
15120     
15121     /**
15122      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15123      * 
15124      */
15125     stylesheets: false,
15126     
15127     // id of frame..
15128     frameId: false,
15129     
15130     // private properties
15131     validationEvent : false,
15132     deferHeight: true,
15133     initialized : false,
15134     activated : false,
15135     sourceEditMode : false,
15136     onFocus : Roo.emptyFn,
15137     iframePad:3,
15138     hideMode:'offsets',
15139     
15140     clearUp: true,
15141     
15142      
15143     
15144
15145     /**
15146      * Protected method that will not generally be called directly. It
15147      * is called when the editor initializes the iframe with HTML contents. Override this method if you
15148      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
15149      */
15150     getDocMarkup : function(){
15151         // body styles..
15152         var st = '';
15153         Roo.log(this.stylesheets);
15154         
15155         // inherit styels from page...?? 
15156         if (this.stylesheets === false) {
15157             
15158             Roo.get(document.head).select('style').each(function(node) {
15159                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
15160             });
15161             
15162             Roo.get(document.head).select('link').each(function(node) { 
15163                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
15164             });
15165             
15166         } else if (!this.stylesheets.length) {
15167                 // simple..
15168                 st = '<style type="text/css">' +
15169                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
15170                    '</style>';
15171         } else {
15172             Roo.each(this.stylesheets, function(s) {
15173                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
15174             });
15175             
15176         }
15177         
15178         st +=  '<style type="text/css">' +
15179             'IMG { cursor: pointer } ' +
15180         '</style>';
15181
15182         
15183         return '<html><head>' + st  +
15184             //<style type="text/css">' +
15185             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
15186             //'</style>' +
15187             ' </head><body class="roo-htmleditor-body"></body></html>';
15188     },
15189
15190     // private
15191     onRender : function(ct, position)
15192     {
15193         var _t = this;
15194         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
15195         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
15196         
15197         
15198         this.el.dom.style.border = '0 none';
15199         this.el.dom.setAttribute('tabIndex', -1);
15200         this.el.addClass('x-hidden hide');
15201         
15202         
15203         
15204         if(Roo.isIE){ // fix IE 1px bogus margin
15205             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
15206         }
15207        
15208         
15209         this.frameId = Roo.id();
15210         
15211          
15212         
15213         var iframe = this.owner.wrap.createChild({
15214             tag: 'iframe',
15215             cls: 'form-control', // bootstrap..
15216             id: this.frameId,
15217             name: this.frameId,
15218             frameBorder : 'no',
15219             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
15220         }, this.el
15221         );
15222         
15223         
15224         this.iframe = iframe.dom;
15225
15226          this.assignDocWin();
15227         
15228         this.doc.designMode = 'on';
15229        
15230         this.doc.open();
15231         this.doc.write(this.getDocMarkup());
15232         this.doc.close();
15233
15234         
15235         var task = { // must defer to wait for browser to be ready
15236             run : function(){
15237                 //console.log("run task?" + this.doc.readyState);
15238                 this.assignDocWin();
15239                 if(this.doc.body || this.doc.readyState == 'complete'){
15240                     try {
15241                         this.doc.designMode="on";
15242                     } catch (e) {
15243                         return;
15244                     }
15245                     Roo.TaskMgr.stop(task);
15246                     this.initEditor.defer(10, this);
15247                 }
15248             },
15249             interval : 10,
15250             duration: 10000,
15251             scope: this
15252         };
15253         Roo.TaskMgr.start(task);
15254
15255         
15256          
15257     },
15258
15259     // private
15260     onResize : function(w, h)
15261     {
15262          Roo.log('resize: ' +w + ',' + h );
15263         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
15264         if(!this.iframe){
15265             return;
15266         }
15267         if(typeof w == 'number'){
15268             
15269             this.iframe.style.width = w + 'px';
15270         }
15271         if(typeof h == 'number'){
15272             
15273             this.iframe.style.height = h + 'px';
15274             if(this.doc){
15275                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
15276             }
15277         }
15278         
15279     },
15280
15281     /**
15282      * Toggles the editor between standard and source edit mode.
15283      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15284      */
15285     toggleSourceEdit : function(sourceEditMode){
15286         
15287         this.sourceEditMode = sourceEditMode === true;
15288         
15289         if(this.sourceEditMode){
15290  
15291             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
15292             
15293         }else{
15294             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
15295             //this.iframe.className = '';
15296             this.deferFocus();
15297         }
15298         //this.setSize(this.owner.wrap.getSize());
15299         //this.fireEvent('editmodechange', this, this.sourceEditMode);
15300     },
15301
15302     
15303   
15304
15305     /**
15306      * Protected method that will not generally be called directly. If you need/want
15307      * custom HTML cleanup, this is the method you should override.
15308      * @param {String} html The HTML to be cleaned
15309      * return {String} The cleaned HTML
15310      */
15311     cleanHtml : function(html){
15312         html = String(html);
15313         if(html.length > 5){
15314             if(Roo.isSafari){ // strip safari nonsense
15315                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
15316             }
15317         }
15318         if(html == '&nbsp;'){
15319             html = '';
15320         }
15321         return html;
15322     },
15323
15324     /**
15325      * HTML Editor -> Textarea
15326      * Protected method that will not generally be called directly. Syncs the contents
15327      * of the editor iframe with the textarea.
15328      */
15329     syncValue : function(){
15330         if(this.initialized){
15331             var bd = (this.doc.body || this.doc.documentElement);
15332             //this.cleanUpPaste(); -- this is done else where and causes havoc..
15333             var html = bd.innerHTML;
15334             if(Roo.isSafari){
15335                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
15336                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
15337                 if(m && m[1]){
15338                     html = '<div style="'+m[0]+'">' + html + '</div>';
15339                 }
15340             }
15341             html = this.cleanHtml(html);
15342             // fix up the special chars.. normaly like back quotes in word...
15343             // however we do not want to do this with chinese..
15344             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
15345                 var cc = b.charCodeAt();
15346                 if (
15347                     (cc >= 0x4E00 && cc < 0xA000 ) ||
15348                     (cc >= 0x3400 && cc < 0x4E00 ) ||
15349                     (cc >= 0xf900 && cc < 0xfb00 )
15350                 ) {
15351                         return b;
15352                 }
15353                 return "&#"+cc+";" 
15354             });
15355             if(this.owner.fireEvent('beforesync', this, html) !== false){
15356                 this.el.dom.value = html;
15357                 this.owner.fireEvent('sync', this, html);
15358             }
15359         }
15360     },
15361
15362     /**
15363      * Protected method that will not generally be called directly. Pushes the value of the textarea
15364      * into the iframe editor.
15365      */
15366     pushValue : function(){
15367         if(this.initialized){
15368             var v = this.el.dom.value.trim();
15369             
15370 //            if(v.length < 1){
15371 //                v = '&#160;';
15372 //            }
15373             
15374             if(this.owner.fireEvent('beforepush', this, v) !== false){
15375                 var d = (this.doc.body || this.doc.documentElement);
15376                 d.innerHTML = v;
15377                 this.cleanUpPaste();
15378                 this.el.dom.value = d.innerHTML;
15379                 this.owner.fireEvent('push', this, v);
15380             }
15381         }
15382     },
15383
15384     // private
15385     deferFocus : function(){
15386         this.focus.defer(10, this);
15387     },
15388
15389     // doc'ed in Field
15390     focus : function(){
15391         if(this.win && !this.sourceEditMode){
15392             this.win.focus();
15393         }else{
15394             this.el.focus();
15395         }
15396     },
15397     
15398     assignDocWin: function()
15399     {
15400         var iframe = this.iframe;
15401         
15402          if(Roo.isIE){
15403             this.doc = iframe.contentWindow.document;
15404             this.win = iframe.contentWindow;
15405         } else {
15406             if (!Roo.get(this.frameId)) {
15407                 return;
15408             }
15409             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
15410             this.win = Roo.get(this.frameId).dom.contentWindow;
15411         }
15412     },
15413     
15414     // private
15415     initEditor : function(){
15416         //console.log("INIT EDITOR");
15417         this.assignDocWin();
15418         
15419         
15420         
15421         this.doc.designMode="on";
15422         this.doc.open();
15423         this.doc.write(this.getDocMarkup());
15424         this.doc.close();
15425         
15426         var dbody = (this.doc.body || this.doc.documentElement);
15427         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
15428         // this copies styles from the containing element into thsi one..
15429         // not sure why we need all of this..
15430         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
15431         
15432         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
15433         //ss['background-attachment'] = 'fixed'; // w3c
15434         dbody.bgProperties = 'fixed'; // ie
15435         //Roo.DomHelper.applyStyles(dbody, ss);
15436         Roo.EventManager.on(this.doc, {
15437             //'mousedown': this.onEditorEvent,
15438             'mouseup': this.onEditorEvent,
15439             'dblclick': this.onEditorEvent,
15440             'click': this.onEditorEvent,
15441             'keyup': this.onEditorEvent,
15442             buffer:100,
15443             scope: this
15444         });
15445         if(Roo.isGecko){
15446             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
15447         }
15448         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
15449             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
15450         }
15451         this.initialized = true;
15452
15453         this.owner.fireEvent('initialize', this);
15454         this.pushValue();
15455     },
15456
15457     // private
15458     onDestroy : function(){
15459         
15460         
15461         
15462         if(this.rendered){
15463             
15464             //for (var i =0; i < this.toolbars.length;i++) {
15465             //    // fixme - ask toolbars for heights?
15466             //    this.toolbars[i].onDestroy();
15467            // }
15468             
15469             //this.wrap.dom.innerHTML = '';
15470             //this.wrap.remove();
15471         }
15472     },
15473
15474     // private
15475     onFirstFocus : function(){
15476         
15477         this.assignDocWin();
15478         
15479         
15480         this.activated = true;
15481          
15482     
15483         if(Roo.isGecko){ // prevent silly gecko errors
15484             this.win.focus();
15485             var s = this.win.getSelection();
15486             if(!s.focusNode || s.focusNode.nodeType != 3){
15487                 var r = s.getRangeAt(0);
15488                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
15489                 r.collapse(true);
15490                 this.deferFocus();
15491             }
15492             try{
15493                 this.execCmd('useCSS', true);
15494                 this.execCmd('styleWithCSS', false);
15495             }catch(e){}
15496         }
15497         this.owner.fireEvent('activate', this);
15498     },
15499
15500     // private
15501     adjustFont: function(btn){
15502         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
15503         //if(Roo.isSafari){ // safari
15504         //    adjust *= 2;
15505        // }
15506         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
15507         if(Roo.isSafari){ // safari
15508             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
15509             v =  (v < 10) ? 10 : v;
15510             v =  (v > 48) ? 48 : v;
15511             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
15512             
15513         }
15514         
15515         
15516         v = Math.max(1, v+adjust);
15517         
15518         this.execCmd('FontSize', v  );
15519     },
15520
15521     onEditorEvent : function(e){
15522         this.owner.fireEvent('editorevent', this, e);
15523       //  this.updateToolbar();
15524         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
15525     },
15526
15527     insertTag : function(tg)
15528     {
15529         // could be a bit smarter... -> wrap the current selected tRoo..
15530         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
15531             
15532             range = this.createRange(this.getSelection());
15533             var wrappingNode = this.doc.createElement(tg.toLowerCase());
15534             wrappingNode.appendChild(range.extractContents());
15535             range.insertNode(wrappingNode);
15536
15537             return;
15538             
15539             
15540             
15541         }
15542         this.execCmd("formatblock",   tg);
15543         
15544     },
15545     
15546     insertText : function(txt)
15547     {
15548         
15549         
15550         var range = this.createRange();
15551         range.deleteContents();
15552                //alert(Sender.getAttribute('label'));
15553                
15554         range.insertNode(this.doc.createTextNode(txt));
15555     } ,
15556     
15557      
15558
15559     /**
15560      * Executes a Midas editor command on the editor document and performs necessary focus and
15561      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
15562      * @param {String} cmd The Midas command
15563      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
15564      */
15565     relayCmd : function(cmd, value){
15566         this.win.focus();
15567         this.execCmd(cmd, value);
15568         this.owner.fireEvent('editorevent', this);
15569         //this.updateToolbar();
15570         this.owner.deferFocus();
15571     },
15572
15573     /**
15574      * Executes a Midas editor command directly on the editor document.
15575      * For visual commands, you should use {@link #relayCmd} instead.
15576      * <b>This should only be called after the editor is initialized.</b>
15577      * @param {String} cmd The Midas command
15578      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
15579      */
15580     execCmd : function(cmd, value){
15581         this.doc.execCommand(cmd, false, value === undefined ? null : value);
15582         this.syncValue();
15583     },
15584  
15585  
15586    
15587     /**
15588      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
15589      * to insert tRoo.
15590      * @param {String} text | dom node.. 
15591      */
15592     insertAtCursor : function(text)
15593     {
15594         
15595         
15596         
15597         if(!this.activated){
15598             return;
15599         }
15600         /*
15601         if(Roo.isIE){
15602             this.win.focus();
15603             var r = this.doc.selection.createRange();
15604             if(r){
15605                 r.collapse(true);
15606                 r.pasteHTML(text);
15607                 this.syncValue();
15608                 this.deferFocus();
15609             
15610             }
15611             return;
15612         }
15613         */
15614         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
15615             this.win.focus();
15616             
15617             
15618             // from jquery ui (MIT licenced)
15619             var range, node;
15620             var win = this.win;
15621             
15622             if (win.getSelection && win.getSelection().getRangeAt) {
15623                 range = win.getSelection().getRangeAt(0);
15624                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
15625                 range.insertNode(node);
15626             } else if (win.document.selection && win.document.selection.createRange) {
15627                 // no firefox support
15628                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
15629                 win.document.selection.createRange().pasteHTML(txt);
15630             } else {
15631                 // no firefox support
15632                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
15633                 this.execCmd('InsertHTML', txt);
15634             } 
15635             
15636             this.syncValue();
15637             
15638             this.deferFocus();
15639         }
15640     },
15641  // private
15642     mozKeyPress : function(e){
15643         if(e.ctrlKey){
15644             var c = e.getCharCode(), cmd;
15645           
15646             if(c > 0){
15647                 c = String.fromCharCode(c).toLowerCase();
15648                 switch(c){
15649                     case 'b':
15650                         cmd = 'bold';
15651                         break;
15652                     case 'i':
15653                         cmd = 'italic';
15654                         break;
15655                     
15656                     case 'u':
15657                         cmd = 'underline';
15658                         break;
15659                     
15660                     case 'v':
15661                         this.cleanUpPaste.defer(100, this);
15662                         return;
15663                         
15664                 }
15665                 if(cmd){
15666                     this.win.focus();
15667                     this.execCmd(cmd);
15668                     this.deferFocus();
15669                     e.preventDefault();
15670                 }
15671                 
15672             }
15673         }
15674     },
15675
15676     // private
15677     fixKeys : function(){ // load time branching for fastest keydown performance
15678         if(Roo.isIE){
15679             return function(e){
15680                 var k = e.getKey(), r;
15681                 if(k == e.TAB){
15682                     e.stopEvent();
15683                     r = this.doc.selection.createRange();
15684                     if(r){
15685                         r.collapse(true);
15686                         r.pasteHTML('&#160;&#160;&#160;&#160;');
15687                         this.deferFocus();
15688                     }
15689                     return;
15690                 }
15691                 
15692                 if(k == e.ENTER){
15693                     r = this.doc.selection.createRange();
15694                     if(r){
15695                         var target = r.parentElement();
15696                         if(!target || target.tagName.toLowerCase() != 'li'){
15697                             e.stopEvent();
15698                             r.pasteHTML('<br />');
15699                             r.collapse(false);
15700                             r.select();
15701                         }
15702                     }
15703                 }
15704                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15705                     this.cleanUpPaste.defer(100, this);
15706                     return;
15707                 }
15708                 
15709                 
15710             };
15711         }else if(Roo.isOpera){
15712             return function(e){
15713                 var k = e.getKey();
15714                 if(k == e.TAB){
15715                     e.stopEvent();
15716                     this.win.focus();
15717                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
15718                     this.deferFocus();
15719                 }
15720                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15721                     this.cleanUpPaste.defer(100, this);
15722                     return;
15723                 }
15724                 
15725             };
15726         }else if(Roo.isSafari){
15727             return function(e){
15728                 var k = e.getKey();
15729                 
15730                 if(k == e.TAB){
15731                     e.stopEvent();
15732                     this.execCmd('InsertText','\t');
15733                     this.deferFocus();
15734                     return;
15735                 }
15736                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15737                     this.cleanUpPaste.defer(100, this);
15738                     return;
15739                 }
15740                 
15741              };
15742         }
15743     }(),
15744     
15745     getAllAncestors: function()
15746     {
15747         var p = this.getSelectedNode();
15748         var a = [];
15749         if (!p) {
15750             a.push(p); // push blank onto stack..
15751             p = this.getParentElement();
15752         }
15753         
15754         
15755         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
15756             a.push(p);
15757             p = p.parentNode;
15758         }
15759         a.push(this.doc.body);
15760         return a;
15761     },
15762     lastSel : false,
15763     lastSelNode : false,
15764     
15765     
15766     getSelection : function() 
15767     {
15768         this.assignDocWin();
15769         return Roo.isIE ? this.doc.selection : this.win.getSelection();
15770     },
15771     
15772     getSelectedNode: function() 
15773     {
15774         // this may only work on Gecko!!!
15775         
15776         // should we cache this!!!!
15777         
15778         
15779         
15780          
15781         var range = this.createRange(this.getSelection()).cloneRange();
15782         
15783         if (Roo.isIE) {
15784             var parent = range.parentElement();
15785             while (true) {
15786                 var testRange = range.duplicate();
15787                 testRange.moveToElementText(parent);
15788                 if (testRange.inRange(range)) {
15789                     break;
15790                 }
15791                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
15792                     break;
15793                 }
15794                 parent = parent.parentElement;
15795             }
15796             return parent;
15797         }
15798         
15799         // is ancestor a text element.
15800         var ac =  range.commonAncestorContainer;
15801         if (ac.nodeType == 3) {
15802             ac = ac.parentNode;
15803         }
15804         
15805         var ar = ac.childNodes;
15806          
15807         var nodes = [];
15808         var other_nodes = [];
15809         var has_other_nodes = false;
15810         for (var i=0;i<ar.length;i++) {
15811             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
15812                 continue;
15813             }
15814             // fullly contained node.
15815             
15816             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
15817                 nodes.push(ar[i]);
15818                 continue;
15819             }
15820             
15821             // probably selected..
15822             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
15823                 other_nodes.push(ar[i]);
15824                 continue;
15825             }
15826             // outer..
15827             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
15828                 continue;
15829             }
15830             
15831             
15832             has_other_nodes = true;
15833         }
15834         if (!nodes.length && other_nodes.length) {
15835             nodes= other_nodes;
15836         }
15837         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
15838             return false;
15839         }
15840         
15841         return nodes[0];
15842     },
15843     createRange: function(sel)
15844     {
15845         // this has strange effects when using with 
15846         // top toolbar - not sure if it's a great idea.
15847         //this.editor.contentWindow.focus();
15848         if (typeof sel != "undefined") {
15849             try {
15850                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
15851             } catch(e) {
15852                 return this.doc.createRange();
15853             }
15854         } else {
15855             return this.doc.createRange();
15856         }
15857     },
15858     getParentElement: function()
15859     {
15860         
15861         this.assignDocWin();
15862         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
15863         
15864         var range = this.createRange(sel);
15865          
15866         try {
15867             var p = range.commonAncestorContainer;
15868             while (p.nodeType == 3) { // text node
15869                 p = p.parentNode;
15870             }
15871             return p;
15872         } catch (e) {
15873             return null;
15874         }
15875     
15876     },
15877     /***
15878      *
15879      * Range intersection.. the hard stuff...
15880      *  '-1' = before
15881      *  '0' = hits..
15882      *  '1' = after.
15883      *         [ -- selected range --- ]
15884      *   [fail]                        [fail]
15885      *
15886      *    basically..
15887      *      if end is before start or  hits it. fail.
15888      *      if start is after end or hits it fail.
15889      *
15890      *   if either hits (but other is outside. - then it's not 
15891      *   
15892      *    
15893      **/
15894     
15895     
15896     // @see http://www.thismuchiknow.co.uk/?p=64.
15897     rangeIntersectsNode : function(range, node)
15898     {
15899         var nodeRange = node.ownerDocument.createRange();
15900         try {
15901             nodeRange.selectNode(node);
15902         } catch (e) {
15903             nodeRange.selectNodeContents(node);
15904         }
15905     
15906         var rangeStartRange = range.cloneRange();
15907         rangeStartRange.collapse(true);
15908     
15909         var rangeEndRange = range.cloneRange();
15910         rangeEndRange.collapse(false);
15911     
15912         var nodeStartRange = nodeRange.cloneRange();
15913         nodeStartRange.collapse(true);
15914     
15915         var nodeEndRange = nodeRange.cloneRange();
15916         nodeEndRange.collapse(false);
15917     
15918         return rangeStartRange.compareBoundaryPoints(
15919                  Range.START_TO_START, nodeEndRange) == -1 &&
15920                rangeEndRange.compareBoundaryPoints(
15921                  Range.START_TO_START, nodeStartRange) == 1;
15922         
15923          
15924     },
15925     rangeCompareNode : function(range, node)
15926     {
15927         var nodeRange = node.ownerDocument.createRange();
15928         try {
15929             nodeRange.selectNode(node);
15930         } catch (e) {
15931             nodeRange.selectNodeContents(node);
15932         }
15933         
15934         
15935         range.collapse(true);
15936     
15937         nodeRange.collapse(true);
15938      
15939         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
15940         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
15941          
15942         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
15943         
15944         var nodeIsBefore   =  ss == 1;
15945         var nodeIsAfter    = ee == -1;
15946         
15947         if (nodeIsBefore && nodeIsAfter)
15948             return 0; // outer
15949         if (!nodeIsBefore && nodeIsAfter)
15950             return 1; //right trailed.
15951         
15952         if (nodeIsBefore && !nodeIsAfter)
15953             return 2;  // left trailed.
15954         // fully contined.
15955         return 3;
15956     },
15957
15958     // private? - in a new class?
15959     cleanUpPaste :  function()
15960     {
15961         // cleans up the whole document..
15962         Roo.log('cleanuppaste');
15963         
15964         this.cleanUpChildren(this.doc.body);
15965         var clean = this.cleanWordChars(this.doc.body.innerHTML);
15966         if (clean != this.doc.body.innerHTML) {
15967             this.doc.body.innerHTML = clean;
15968         }
15969         
15970     },
15971     
15972     cleanWordChars : function(input) {// change the chars to hex code
15973         var he = Roo.HtmlEditorCore;
15974         
15975         var output = input;
15976         Roo.each(he.swapCodes, function(sw) { 
15977             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
15978             
15979             output = output.replace(swapper, sw[1]);
15980         });
15981         
15982         return output;
15983     },
15984     
15985     
15986     cleanUpChildren : function (n)
15987     {
15988         if (!n.childNodes.length) {
15989             return;
15990         }
15991         for (var i = n.childNodes.length-1; i > -1 ; i--) {
15992            this.cleanUpChild(n.childNodes[i]);
15993         }
15994     },
15995     
15996     
15997         
15998     
15999     cleanUpChild : function (node)
16000     {
16001         var ed = this;
16002         //console.log(node);
16003         if (node.nodeName == "#text") {
16004             // clean up silly Windows -- stuff?
16005             return; 
16006         }
16007         if (node.nodeName == "#comment") {
16008             node.parentNode.removeChild(node);
16009             // clean up silly Windows -- stuff?
16010             return; 
16011         }
16012         
16013         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
16014             // remove node.
16015             node.parentNode.removeChild(node);
16016             return;
16017             
16018         }
16019         
16020         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
16021         
16022         // remove <a name=....> as rendering on yahoo mailer is borked with this.
16023         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
16024         
16025         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
16026         //    remove_keep_children = true;
16027         //}
16028         
16029         if (remove_keep_children) {
16030             this.cleanUpChildren(node);
16031             // inserts everything just before this node...
16032             while (node.childNodes.length) {
16033                 var cn = node.childNodes[0];
16034                 node.removeChild(cn);
16035                 node.parentNode.insertBefore(cn, node);
16036             }
16037             node.parentNode.removeChild(node);
16038             return;
16039         }
16040         
16041         if (!node.attributes || !node.attributes.length) {
16042             this.cleanUpChildren(node);
16043             return;
16044         }
16045         
16046         function cleanAttr(n,v)
16047         {
16048             
16049             if (v.match(/^\./) || v.match(/^\//)) {
16050                 return;
16051             }
16052             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
16053                 return;
16054             }
16055             if (v.match(/^#/)) {
16056                 return;
16057             }
16058 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
16059             node.removeAttribute(n);
16060             
16061         }
16062         
16063         function cleanStyle(n,v)
16064         {
16065             if (v.match(/expression/)) { //XSS?? should we even bother..
16066                 node.removeAttribute(n);
16067                 return;
16068             }
16069             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
16070             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
16071             
16072             
16073             var parts = v.split(/;/);
16074             var clean = [];
16075             
16076             Roo.each(parts, function(p) {
16077                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
16078                 if (!p.length) {
16079                     return true;
16080                 }
16081                 var l = p.split(':').shift().replace(/\s+/g,'');
16082                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
16083                 
16084                 if ( cblack.indexOf(l) > -1) {
16085 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
16086                     //node.removeAttribute(n);
16087                     return true;
16088                 }
16089                 //Roo.log()
16090                 // only allow 'c whitelisted system attributes'
16091                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
16092 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
16093                     //node.removeAttribute(n);
16094                     return true;
16095                 }
16096                 
16097                 
16098                  
16099                 
16100                 clean.push(p);
16101                 return true;
16102             });
16103             if (clean.length) { 
16104                 node.setAttribute(n, clean.join(';'));
16105             } else {
16106                 node.removeAttribute(n);
16107             }
16108             
16109         }
16110         
16111         
16112         for (var i = node.attributes.length-1; i > -1 ; i--) {
16113             var a = node.attributes[i];
16114             //console.log(a);
16115             
16116             if (a.name.toLowerCase().substr(0,2)=='on')  {
16117                 node.removeAttribute(a.name);
16118                 continue;
16119             }
16120             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
16121                 node.removeAttribute(a.name);
16122                 continue;
16123             }
16124             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
16125                 cleanAttr(a.name,a.value); // fixme..
16126                 continue;
16127             }
16128             if (a.name == 'style') {
16129                 cleanStyle(a.name,a.value);
16130                 continue;
16131             }
16132             /// clean up MS crap..
16133             // tecnically this should be a list of valid class'es..
16134             
16135             
16136             if (a.name == 'class') {
16137                 if (a.value.match(/^Mso/)) {
16138                     node.className = '';
16139                 }
16140                 
16141                 if (a.value.match(/body/)) {
16142                     node.className = '';
16143                 }
16144                 continue;
16145             }
16146             
16147             // style cleanup!?
16148             // class cleanup?
16149             
16150         }
16151         
16152         
16153         this.cleanUpChildren(node);
16154         
16155         
16156     },
16157     /**
16158      * Clean up MS wordisms...
16159      */
16160     cleanWord : function(node)
16161     {
16162         var _t = this;
16163         var cleanWordChildren = function()
16164         {
16165             if (!node.childNodes.length) {
16166                 return;
16167             }
16168             for (var i = node.childNodes.length-1; i > -1 ; i--) {
16169                _t.cleanWord(node.childNodes[i]);
16170             }
16171         }
16172         
16173         
16174         if (!node) {
16175             this.cleanWord(this.doc.body);
16176             return;
16177         }
16178         if (node.nodeName == "#text") {
16179             // clean up silly Windows -- stuff?
16180             return; 
16181         }
16182         if (node.nodeName == "#comment") {
16183             node.parentNode.removeChild(node);
16184             // clean up silly Windows -- stuff?
16185             return; 
16186         }
16187         
16188         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
16189             node.parentNode.removeChild(node);
16190             return;
16191         }
16192         
16193         // remove - but keep children..
16194         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
16195             while (node.childNodes.length) {
16196                 var cn = node.childNodes[0];
16197                 node.removeChild(cn);
16198                 node.parentNode.insertBefore(cn, node);
16199             }
16200             node.parentNode.removeChild(node);
16201             cleanWordChildren();
16202             return;
16203         }
16204         // clean styles
16205         if (node.className.length) {
16206             
16207             var cn = node.className.split(/\W+/);
16208             var cna = [];
16209             Roo.each(cn, function(cls) {
16210                 if (cls.match(/Mso[a-zA-Z]+/)) {
16211                     return;
16212                 }
16213                 cna.push(cls);
16214             });
16215             node.className = cna.length ? cna.join(' ') : '';
16216             if (!cna.length) {
16217                 node.removeAttribute("class");
16218             }
16219         }
16220         
16221         if (node.hasAttribute("lang")) {
16222             node.removeAttribute("lang");
16223         }
16224         
16225         if (node.hasAttribute("style")) {
16226             
16227             var styles = node.getAttribute("style").split(";");
16228             var nstyle = [];
16229             Roo.each(styles, function(s) {
16230                 if (!s.match(/:/)) {
16231                     return;
16232                 }
16233                 var kv = s.split(":");
16234                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
16235                     return;
16236                 }
16237                 // what ever is left... we allow.
16238                 nstyle.push(s);
16239             });
16240             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
16241             if (!nstyle.length) {
16242                 node.removeAttribute('style');
16243             }
16244         }
16245         
16246         cleanWordChildren();
16247         
16248         
16249     },
16250     domToHTML : function(currentElement, depth, nopadtext) {
16251         
16252             depth = depth || 0;
16253             nopadtext = nopadtext || false;
16254         
16255             if (!currentElement) {
16256                 return this.domToHTML(this.doc.body);
16257             }
16258             
16259             //Roo.log(currentElement);
16260             var j;
16261             var allText = false;
16262             var nodeName = currentElement.nodeName;
16263             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
16264             
16265             if  (nodeName == '#text') {
16266                 return currentElement.nodeValue;
16267             }
16268             
16269             
16270             var ret = '';
16271             if (nodeName != 'BODY') {
16272                  
16273                 var i = 0;
16274                 // Prints the node tagName, such as <A>, <IMG>, etc
16275                 if (tagName) {
16276                     var attr = [];
16277                     for(i = 0; i < currentElement.attributes.length;i++) {
16278                         // quoting?
16279                         var aname = currentElement.attributes.item(i).name;
16280                         if (!currentElement.attributes.item(i).value.length) {
16281                             continue;
16282                         }
16283                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
16284                     }
16285                     
16286                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
16287                 } 
16288                 else {
16289                     
16290                     // eack
16291                 }
16292             } else {
16293                 tagName = false;
16294             }
16295             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
16296                 return ret;
16297             }
16298             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
16299                 nopadtext = true;
16300             }
16301             
16302             
16303             // Traverse the tree
16304             i = 0;
16305             var currentElementChild = currentElement.childNodes.item(i);
16306             var allText = true;
16307             var innerHTML  = '';
16308             lastnode = '';
16309             while (currentElementChild) {
16310                 // Formatting code (indent the tree so it looks nice on the screen)
16311                 var nopad = nopadtext;
16312                 if (lastnode == 'SPAN') {
16313                     nopad  = true;
16314                 }
16315                 // text
16316                 if  (currentElementChild.nodeName == '#text') {
16317                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
16318                     if (!nopad && toadd.length > 80) {
16319                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
16320                     }
16321                     innerHTML  += toadd;
16322                     
16323                     i++;
16324                     currentElementChild = currentElement.childNodes.item(i);
16325                     lastNode = '';
16326                     continue;
16327                 }
16328                 allText = false;
16329                 
16330                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
16331                     
16332                 // Recursively traverse the tree structure of the child node
16333                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
16334                 lastnode = currentElementChild.nodeName;
16335                 i++;
16336                 currentElementChild=currentElement.childNodes.item(i);
16337             }
16338             
16339             ret += innerHTML;
16340             
16341             if (!allText) {
16342                     // The remaining code is mostly for formatting the tree
16343                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
16344             }
16345             
16346             
16347             if (tagName) {
16348                 ret+= "</"+tagName+">";
16349             }
16350             return ret;
16351             
16352         }
16353     
16354     // hide stuff that is not compatible
16355     /**
16356      * @event blur
16357      * @hide
16358      */
16359     /**
16360      * @event change
16361      * @hide
16362      */
16363     /**
16364      * @event focus
16365      * @hide
16366      */
16367     /**
16368      * @event specialkey
16369      * @hide
16370      */
16371     /**
16372      * @cfg {String} fieldClass @hide
16373      */
16374     /**
16375      * @cfg {String} focusClass @hide
16376      */
16377     /**
16378      * @cfg {String} autoCreate @hide
16379      */
16380     /**
16381      * @cfg {String} inputType @hide
16382      */
16383     /**
16384      * @cfg {String} invalidClass @hide
16385      */
16386     /**
16387      * @cfg {String} invalidText @hide
16388      */
16389     /**
16390      * @cfg {String} msgFx @hide
16391      */
16392     /**
16393      * @cfg {String} validateOnBlur @hide
16394      */
16395 });
16396
16397 Roo.HtmlEditorCore.white = [
16398         'area', 'br', 'img', 'input', 'hr', 'wbr',
16399         
16400        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
16401        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
16402        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
16403        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
16404        'table',   'ul',         'xmp', 
16405        
16406        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
16407       'thead',   'tr', 
16408      
16409       'dir', 'menu', 'ol', 'ul', 'dl',
16410        
16411       'embed',  'object'
16412 ];
16413
16414
16415 Roo.HtmlEditorCore.black = [
16416     //    'embed',  'object', // enable - backend responsiblity to clean thiese
16417         'applet', // 
16418         'base',   'basefont', 'bgsound', 'blink',  'body', 
16419         'frame',  'frameset', 'head',    'html',   'ilayer', 
16420         'iframe', 'layer',  'link',     'meta',    'object',   
16421         'script', 'style' ,'title',  'xml' // clean later..
16422 ];
16423 Roo.HtmlEditorCore.clean = [
16424     'script', 'style', 'title', 'xml'
16425 ];
16426 Roo.HtmlEditorCore.remove = [
16427     'font'
16428 ];
16429 // attributes..
16430
16431 Roo.HtmlEditorCore.ablack = [
16432     'on'
16433 ];
16434     
16435 Roo.HtmlEditorCore.aclean = [ 
16436     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
16437 ];
16438
16439 // protocols..
16440 Roo.HtmlEditorCore.pwhite= [
16441         'http',  'https',  'mailto'
16442 ];
16443
16444 // white listed style attributes.
16445 Roo.HtmlEditorCore.cwhite= [
16446       //  'text-align', /// default is to allow most things..
16447       
16448          
16449 //        'font-size'//??
16450 ];
16451
16452 // black listed style attributes.
16453 Roo.HtmlEditorCore.cblack= [
16454       //  'font-size' -- this can be set by the project 
16455 ];
16456
16457
16458 Roo.HtmlEditorCore.swapCodes   =[ 
16459     [    8211, "--" ], 
16460     [    8212, "--" ], 
16461     [    8216,  "'" ],  
16462     [    8217, "'" ],  
16463     [    8220, '"' ],  
16464     [    8221, '"' ],  
16465     [    8226, "*" ],  
16466     [    8230, "..." ]
16467 ]; 
16468
16469     /*
16470  * - LGPL
16471  *
16472  * HtmlEditor
16473  * 
16474  */
16475
16476 /**
16477  * @class Roo.bootstrap.HtmlEditor
16478  * @extends Roo.bootstrap.TextArea
16479  * Bootstrap HtmlEditor class
16480
16481  * @constructor
16482  * Create a new HtmlEditor
16483  * @param {Object} config The config object
16484  */
16485
16486 Roo.bootstrap.HtmlEditor = function(config){
16487     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
16488     if (!this.toolbars) {
16489         this.toolbars = [];
16490     }
16491     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
16492     this.addEvents({
16493             /**
16494              * @event initialize
16495              * Fires when the editor is fully initialized (including the iframe)
16496              * @param {HtmlEditor} this
16497              */
16498             initialize: true,
16499             /**
16500              * @event activate
16501              * Fires when the editor is first receives the focus. Any insertion must wait
16502              * until after this event.
16503              * @param {HtmlEditor} this
16504              */
16505             activate: true,
16506              /**
16507              * @event beforesync
16508              * Fires before the textarea is updated with content from the editor iframe. Return false
16509              * to cancel the sync.
16510              * @param {HtmlEditor} this
16511              * @param {String} html
16512              */
16513             beforesync: true,
16514              /**
16515              * @event beforepush
16516              * Fires before the iframe editor is updated with content from the textarea. Return false
16517              * to cancel the push.
16518              * @param {HtmlEditor} this
16519              * @param {String} html
16520              */
16521             beforepush: true,
16522              /**
16523              * @event sync
16524              * Fires when the textarea is updated with content from the editor iframe.
16525              * @param {HtmlEditor} this
16526              * @param {String} html
16527              */
16528             sync: true,
16529              /**
16530              * @event push
16531              * Fires when the iframe editor is updated with content from the textarea.
16532              * @param {HtmlEditor} this
16533              * @param {String} html
16534              */
16535             push: true,
16536              /**
16537              * @event editmodechange
16538              * Fires when the editor switches edit modes
16539              * @param {HtmlEditor} this
16540              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
16541              */
16542             editmodechange: true,
16543             /**
16544              * @event editorevent
16545              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16546              * @param {HtmlEditor} this
16547              */
16548             editorevent: true,
16549             /**
16550              * @event firstfocus
16551              * Fires when on first focus - needed by toolbars..
16552              * @param {HtmlEditor} this
16553              */
16554             firstfocus: true,
16555             /**
16556              * @event autosave
16557              * Auto save the htmlEditor value as a file into Events
16558              * @param {HtmlEditor} this
16559              */
16560             autosave: true,
16561             /**
16562              * @event savedpreview
16563              * preview the saved version of htmlEditor
16564              * @param {HtmlEditor} this
16565              */
16566             savedpreview: true
16567         });
16568 };
16569
16570
16571 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
16572     
16573     
16574       /**
16575      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
16576      */
16577     toolbars : false,
16578    
16579      /**
16580      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16581      *                        Roo.resizable.
16582      */
16583     resizable : false,
16584      /**
16585      * @cfg {Number} height (in pixels)
16586      */   
16587     height: 300,
16588    /**
16589      * @cfg {Number} width (in pixels)
16590      */   
16591     width: false,
16592     
16593     /**
16594      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16595      * 
16596      */
16597     stylesheets: false,
16598     
16599     // id of frame..
16600     frameId: false,
16601     
16602     // private properties
16603     validationEvent : false,
16604     deferHeight: true,
16605     initialized : false,
16606     activated : false,
16607     
16608     onFocus : Roo.emptyFn,
16609     iframePad:3,
16610     hideMode:'offsets',
16611     
16612     
16613     tbContainer : false,
16614     
16615     toolbarContainer :function() {
16616         return this.wrap.select('.x-html-editor-tb',true).first();
16617     },
16618
16619     /**
16620      * Protected method that will not generally be called directly. It
16621      * is called when the editor creates its toolbar. Override this method if you need to
16622      * add custom toolbar buttons.
16623      * @param {HtmlEditor} editor
16624      */
16625     createToolbar : function(){
16626         
16627         Roo.log("create toolbars");
16628         
16629         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
16630         this.toolbars[0].render(this.toolbarContainer());
16631         
16632         return;
16633         
16634 //        if (!editor.toolbars || !editor.toolbars.length) {
16635 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
16636 //        }
16637 //        
16638 //        for (var i =0 ; i < editor.toolbars.length;i++) {
16639 //            editor.toolbars[i] = Roo.factory(
16640 //                    typeof(editor.toolbars[i]) == 'string' ?
16641 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
16642 //                Roo.bootstrap.HtmlEditor);
16643 //            editor.toolbars[i].init(editor);
16644 //        }
16645     },
16646
16647      
16648     // private
16649     onRender : function(ct, position)
16650     {
16651        // Roo.log("Call onRender: " + this.xtype);
16652         var _t = this;
16653         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
16654       
16655         this.wrap = this.inputEl().wrap({
16656             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
16657         });
16658         
16659         this.editorcore.onRender(ct, position);
16660          
16661         if (this.resizable) {
16662             this.resizeEl = new Roo.Resizable(this.wrap, {
16663                 pinned : true,
16664                 wrap: true,
16665                 dynamic : true,
16666                 minHeight : this.height,
16667                 height: this.height,
16668                 handles : this.resizable,
16669                 width: this.width,
16670                 listeners : {
16671                     resize : function(r, w, h) {
16672                         _t.onResize(w,h); // -something
16673                     }
16674                 }
16675             });
16676             
16677         }
16678         this.createToolbar(this);
16679        
16680         
16681         if(!this.width && this.resizable){
16682             this.setSize(this.wrap.getSize());
16683         }
16684         if (this.resizeEl) {
16685             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
16686             // should trigger onReize..
16687         }
16688         
16689     },
16690
16691     // private
16692     onResize : function(w, h)
16693     {
16694         Roo.log('resize: ' +w + ',' + h );
16695         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
16696         var ew = false;
16697         var eh = false;
16698         
16699         if(this.inputEl() ){
16700             if(typeof w == 'number'){
16701                 var aw = w - this.wrap.getFrameWidth('lr');
16702                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
16703                 ew = aw;
16704             }
16705             if(typeof h == 'number'){
16706                  var tbh = -11;  // fixme it needs to tool bar size!
16707                 for (var i =0; i < this.toolbars.length;i++) {
16708                     // fixme - ask toolbars for heights?
16709                     tbh += this.toolbars[i].el.getHeight();
16710                     //if (this.toolbars[i].footer) {
16711                     //    tbh += this.toolbars[i].footer.el.getHeight();
16712                     //}
16713                 }
16714               
16715                 
16716                 
16717                 
16718                 
16719                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
16720                 ah -= 5; // knock a few pixes off for look..
16721                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
16722                 var eh = ah;
16723             }
16724         }
16725         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
16726         this.editorcore.onResize(ew,eh);
16727         
16728     },
16729
16730     /**
16731      * Toggles the editor between standard and source edit mode.
16732      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16733      */
16734     toggleSourceEdit : function(sourceEditMode)
16735     {
16736         this.editorcore.toggleSourceEdit(sourceEditMode);
16737         
16738         if(this.editorcore.sourceEditMode){
16739             Roo.log('editor - showing textarea');
16740             
16741 //            Roo.log('in');
16742 //            Roo.log(this.syncValue());
16743             this.syncValue();
16744             this.inputEl().removeClass('hide');
16745             this.inputEl().dom.removeAttribute('tabIndex');
16746             this.inputEl().focus();
16747         }else{
16748             Roo.log('editor - hiding textarea');
16749 //            Roo.log('out')
16750 //            Roo.log(this.pushValue()); 
16751             this.pushValue();
16752             
16753             this.inputEl().addClass('hide');
16754             this.inputEl().dom.setAttribute('tabIndex', -1);
16755             //this.deferFocus();
16756         }
16757          
16758         if(this.resizable){
16759             this.setSize(this.wrap.getSize());
16760         }
16761         
16762         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
16763     },
16764  
16765     // private (for BoxComponent)
16766     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16767
16768     // private (for BoxComponent)
16769     getResizeEl : function(){
16770         return this.wrap;
16771     },
16772
16773     // private (for BoxComponent)
16774     getPositionEl : function(){
16775         return this.wrap;
16776     },
16777
16778     // private
16779     initEvents : function(){
16780         this.originalValue = this.getValue();
16781     },
16782
16783 //    /**
16784 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16785 //     * @method
16786 //     */
16787 //    markInvalid : Roo.emptyFn,
16788 //    /**
16789 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16790 //     * @method
16791 //     */
16792 //    clearInvalid : Roo.emptyFn,
16793
16794     setValue : function(v){
16795         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
16796         this.editorcore.pushValue();
16797     },
16798
16799      
16800     // private
16801     deferFocus : function(){
16802         this.focus.defer(10, this);
16803     },
16804
16805     // doc'ed in Field
16806     focus : function(){
16807         this.editorcore.focus();
16808         
16809     },
16810       
16811
16812     // private
16813     onDestroy : function(){
16814         
16815         
16816         
16817         if(this.rendered){
16818             
16819             for (var i =0; i < this.toolbars.length;i++) {
16820                 // fixme - ask toolbars for heights?
16821                 this.toolbars[i].onDestroy();
16822             }
16823             
16824             this.wrap.dom.innerHTML = '';
16825             this.wrap.remove();
16826         }
16827     },
16828
16829     // private
16830     onFirstFocus : function(){
16831         //Roo.log("onFirstFocus");
16832         this.editorcore.onFirstFocus();
16833          for (var i =0; i < this.toolbars.length;i++) {
16834             this.toolbars[i].onFirstFocus();
16835         }
16836         
16837     },
16838     
16839     // private
16840     syncValue : function()
16841     {   
16842         this.editorcore.syncValue();
16843     },
16844     
16845     pushValue : function()
16846     {   
16847         this.editorcore.pushValue();
16848     }
16849      
16850     
16851     // hide stuff that is not compatible
16852     /**
16853      * @event blur
16854      * @hide
16855      */
16856     /**
16857      * @event change
16858      * @hide
16859      */
16860     /**
16861      * @event focus
16862      * @hide
16863      */
16864     /**
16865      * @event specialkey
16866      * @hide
16867      */
16868     /**
16869      * @cfg {String} fieldClass @hide
16870      */
16871     /**
16872      * @cfg {String} focusClass @hide
16873      */
16874     /**
16875      * @cfg {String} autoCreate @hide
16876      */
16877     /**
16878      * @cfg {String} inputType @hide
16879      */
16880     /**
16881      * @cfg {String} invalidClass @hide
16882      */
16883     /**
16884      * @cfg {String} invalidText @hide
16885      */
16886     /**
16887      * @cfg {String} msgFx @hide
16888      */
16889     /**
16890      * @cfg {String} validateOnBlur @hide
16891      */
16892 });
16893  
16894     
16895    
16896    
16897    
16898       
16899 Roo.namespace('Roo.bootstrap.htmleditor');
16900 /**
16901  * @class Roo.bootstrap.HtmlEditorToolbar1
16902  * Basic Toolbar
16903  * 
16904  * Usage:
16905  *
16906  new Roo.bootstrap.HtmlEditor({
16907     ....
16908     toolbars : [
16909         new Roo.bootstrap.HtmlEditorToolbar1({
16910             disable : { fonts: 1 , format: 1, ..., ... , ...],
16911             btns : [ .... ]
16912         })
16913     }
16914      
16915  * 
16916  * @cfg {Object} disable List of elements to disable..
16917  * @cfg {Array} btns List of additional buttons.
16918  * 
16919  * 
16920  * NEEDS Extra CSS? 
16921  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
16922  */
16923  
16924 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
16925 {
16926     
16927     Roo.apply(this, config);
16928     
16929     // default disabled, based on 'good practice'..
16930     this.disable = this.disable || {};
16931     Roo.applyIf(this.disable, {
16932         fontSize : true,
16933         colors : true,
16934         specialElements : true
16935     });
16936     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
16937     
16938     this.editor = config.editor;
16939     this.editorcore = config.editor.editorcore;
16940     
16941     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
16942     
16943     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
16944     // dont call parent... till later.
16945 }
16946 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
16947      
16948     bar : true,
16949     
16950     editor : false,
16951     editorcore : false,
16952     
16953     
16954     formats : [
16955         "p" ,  
16956         "h1","h2","h3","h4","h5","h6", 
16957         "pre", "code", 
16958         "abbr", "acronym", "address", "cite", "samp", "var",
16959         'div','span'
16960     ],
16961     
16962     onRender : function(ct, position)
16963     {
16964        // Roo.log("Call onRender: " + this.xtype);
16965         
16966        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
16967        Roo.log(this.el);
16968        this.el.dom.style.marginBottom = '0';
16969        var _this = this;
16970        var editorcore = this.editorcore;
16971        var editor= this.editor;
16972        
16973        var children = [];
16974        var btn = function(id,cmd , toggle, handler){
16975        
16976             var  event = toggle ? 'toggle' : 'click';
16977        
16978             var a = {
16979                 size : 'sm',
16980                 xtype: 'Button',
16981                 xns: Roo.bootstrap,
16982                 glyphicon : id,
16983                 cmd : id || cmd,
16984                 enableToggle:toggle !== false,
16985                 //html : 'submit'
16986                 pressed : toggle ? false : null,
16987                 listeners : {}
16988             }
16989             a.listeners[toggle ? 'toggle' : 'click'] = function() {
16990                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
16991             }
16992             children.push(a);
16993             return a;
16994        }
16995         
16996         var style = {
16997                 xtype: 'Button',
16998                 size : 'sm',
16999                 xns: Roo.bootstrap,
17000                 glyphicon : 'font',
17001                 //html : 'submit'
17002                 menu : {
17003                     xtype: 'Menu',
17004                     xns: Roo.bootstrap,
17005                     items:  []
17006                 }
17007         };
17008         Roo.each(this.formats, function(f) {
17009             style.menu.items.push({
17010                 xtype :'MenuItem',
17011                 xns: Roo.bootstrap,
17012                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
17013                 tagname : f,
17014                 listeners : {
17015                     click : function()
17016                     {
17017                         editorcore.insertTag(this.tagname);
17018                         editor.focus();
17019                     }
17020                 }
17021                 
17022             });
17023         });
17024          children.push(style);   
17025             
17026             
17027         btn('bold',false,true);
17028         btn('italic',false,true);
17029         btn('align-left', 'justifyleft',true);
17030         btn('align-center', 'justifycenter',true);
17031         btn('align-right' , 'justifyright',true);
17032         btn('link', false, false, function(btn) {
17033             //Roo.log("create link?");
17034             var url = prompt(this.createLinkText, this.defaultLinkValue);
17035             if(url && url != 'http:/'+'/'){
17036                 this.editorcore.relayCmd('createlink', url);
17037             }
17038         }),
17039         btn('list','insertunorderedlist',true);
17040         btn('pencil', false,true, function(btn){
17041                 Roo.log(this);
17042                 
17043                 this.toggleSourceEdit(btn.pressed);
17044         });
17045         /*
17046         var cog = {
17047                 xtype: 'Button',
17048                 size : 'sm',
17049                 xns: Roo.bootstrap,
17050                 glyphicon : 'cog',
17051                 //html : 'submit'
17052                 menu : {
17053                     xtype: 'Menu',
17054                     xns: Roo.bootstrap,
17055                     items:  []
17056                 }
17057         };
17058         
17059         cog.menu.items.push({
17060             xtype :'MenuItem',
17061             xns: Roo.bootstrap,
17062             html : Clean styles,
17063             tagname : f,
17064             listeners : {
17065                 click : function()
17066                 {
17067                     editorcore.insertTag(this.tagname);
17068                     editor.focus();
17069                 }
17070             }
17071             
17072         });
17073        */
17074         
17075          
17076        this.xtype = 'NavSimplebar';
17077         
17078         for(var i=0;i< children.length;i++) {
17079             
17080             this.buttons.add(this.addxtypeChild(children[i]));
17081             
17082         }
17083         
17084         editor.on('editorevent', this.updateToolbar, this);
17085     },
17086     onBtnClick : function(id)
17087     {
17088        this.editorcore.relayCmd(id);
17089        this.editorcore.focus();
17090     },
17091     
17092     /**
17093      * Protected method that will not generally be called directly. It triggers
17094      * a toolbar update by reading the markup state of the current selection in the editor.
17095      */
17096     updateToolbar: function(){
17097
17098         if(!this.editorcore.activated){
17099             this.editor.onFirstFocus(); // is this neeed?
17100             return;
17101         }
17102
17103         var btns = this.buttons; 
17104         var doc = this.editorcore.doc;
17105         btns.get('bold').setActive(doc.queryCommandState('bold'));
17106         btns.get('italic').setActive(doc.queryCommandState('italic'));
17107         //btns.get('underline').setActive(doc.queryCommandState('underline'));
17108         
17109         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
17110         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
17111         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
17112         
17113         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
17114         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
17115          /*
17116         
17117         var ans = this.editorcore.getAllAncestors();
17118         if (this.formatCombo) {
17119             
17120             
17121             var store = this.formatCombo.store;
17122             this.formatCombo.setValue("");
17123             for (var i =0; i < ans.length;i++) {
17124                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
17125                     // select it..
17126                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
17127                     break;
17128                 }
17129             }
17130         }
17131         
17132         
17133         
17134         // hides menus... - so this cant be on a menu...
17135         Roo.bootstrap.MenuMgr.hideAll();
17136         */
17137         Roo.bootstrap.MenuMgr.hideAll();
17138         //this.editorsyncValue();
17139     },
17140     onFirstFocus: function() {
17141         this.buttons.each(function(item){
17142            item.enable();
17143         });
17144     },
17145     toggleSourceEdit : function(sourceEditMode){
17146         
17147           
17148         if(sourceEditMode){
17149             Roo.log("disabling buttons");
17150            this.buttons.each( function(item){
17151                 if(item.cmd != 'pencil'){
17152                     item.disable();
17153                 }
17154             });
17155           
17156         }else{
17157             Roo.log("enabling buttons");
17158             if(this.editorcore.initialized){
17159                 this.buttons.each( function(item){
17160                     item.enable();
17161                 });
17162             }
17163             
17164         }
17165         Roo.log("calling toggole on editor");
17166         // tell the editor that it's been pressed..
17167         this.editor.toggleSourceEdit(sourceEditMode);
17168        
17169     }
17170 });
17171
17172
17173
17174
17175
17176 /**
17177  * @class Roo.bootstrap.Table.AbstractSelectionModel
17178  * @extends Roo.util.Observable
17179  * Abstract base class for grid SelectionModels.  It provides the interface that should be
17180  * implemented by descendant classes.  This class should not be directly instantiated.
17181  * @constructor
17182  */
17183 Roo.bootstrap.Table.AbstractSelectionModel = function(){
17184     this.locked = false;
17185     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
17186 };
17187
17188
17189 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
17190     /** @ignore Called by the grid automatically. Do not call directly. */
17191     init : function(grid){
17192         this.grid = grid;
17193         this.initEvents();
17194     },
17195
17196     /**
17197      * Locks the selections.
17198      */
17199     lock : function(){
17200         this.locked = true;
17201     },
17202
17203     /**
17204      * Unlocks the selections.
17205      */
17206     unlock : function(){
17207         this.locked = false;
17208     },
17209
17210     /**
17211      * Returns true if the selections are locked.
17212      * @return {Boolean}
17213      */
17214     isLocked : function(){
17215         return this.locked;
17216     }
17217 });
17218 /**
17219  * @extends Roo.bootstrap.Table.AbstractSelectionModel
17220  * @class Roo.bootstrap.Table.RowSelectionModel
17221  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
17222  * It supports multiple selections and keyboard selection/navigation. 
17223  * @constructor
17224  * @param {Object} config
17225  */
17226
17227 Roo.bootstrap.Table.RowSelectionModel = function(config){
17228     Roo.apply(this, config);
17229     this.selections = new Roo.util.MixedCollection(false, function(o){
17230         return o.id;
17231     });
17232
17233     this.last = false;
17234     this.lastActive = false;
17235
17236     this.addEvents({
17237         /**
17238              * @event selectionchange
17239              * Fires when the selection changes
17240              * @param {SelectionModel} this
17241              */
17242             "selectionchange" : true,
17243         /**
17244              * @event afterselectionchange
17245              * Fires after the selection changes (eg. by key press or clicking)
17246              * @param {SelectionModel} this
17247              */
17248             "afterselectionchange" : true,
17249         /**
17250              * @event beforerowselect
17251              * Fires when a row is selected being selected, return false to cancel.
17252              * @param {SelectionModel} this
17253              * @param {Number} rowIndex The selected index
17254              * @param {Boolean} keepExisting False if other selections will be cleared
17255              */
17256             "beforerowselect" : true,
17257         /**
17258              * @event rowselect
17259              * Fires when a row is selected.
17260              * @param {SelectionModel} this
17261              * @param {Number} rowIndex The selected index
17262              * @param {Roo.data.Record} r The record
17263              */
17264             "rowselect" : true,
17265         /**
17266              * @event rowdeselect
17267              * Fires when a row is deselected.
17268              * @param {SelectionModel} this
17269              * @param {Number} rowIndex The selected index
17270              */
17271         "rowdeselect" : true
17272     });
17273     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
17274     this.locked = false;
17275 };
17276
17277 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
17278     /**
17279      * @cfg {Boolean} singleSelect
17280      * True to allow selection of only one row at a time (defaults to false)
17281      */
17282     singleSelect : false,
17283
17284     // private
17285     initEvents : function(){
17286
17287         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
17288             this.grid.on("mousedown", this.handleMouseDown, this);
17289         }else{ // allow click to work like normal
17290             this.grid.on("rowclick", this.handleDragableRowClick, this);
17291         }
17292
17293         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
17294             "up" : function(e){
17295                 if(!e.shiftKey){
17296                     this.selectPrevious(e.shiftKey);
17297                 }else if(this.last !== false && this.lastActive !== false){
17298                     var last = this.last;
17299                     this.selectRange(this.last,  this.lastActive-1);
17300                     this.grid.getView().focusRow(this.lastActive);
17301                     if(last !== false){
17302                         this.last = last;
17303                     }
17304                 }else{
17305                     this.selectFirstRow();
17306                 }
17307                 this.fireEvent("afterselectionchange", this);
17308             },
17309             "down" : function(e){
17310                 if(!e.shiftKey){
17311                     this.selectNext(e.shiftKey);
17312                 }else if(this.last !== false && this.lastActive !== false){
17313                     var last = this.last;
17314                     this.selectRange(this.last,  this.lastActive+1);
17315                     this.grid.getView().focusRow(this.lastActive);
17316                     if(last !== false){
17317                         this.last = last;
17318                     }
17319                 }else{
17320                     this.selectFirstRow();
17321                 }
17322                 this.fireEvent("afterselectionchange", this);
17323             },
17324             scope: this
17325         });
17326
17327         var view = this.grid.view;
17328         view.on("refresh", this.onRefresh, this);
17329         view.on("rowupdated", this.onRowUpdated, this);
17330         view.on("rowremoved", this.onRemove, this);
17331     },
17332
17333     // private
17334     onRefresh : function(){
17335         var ds = this.grid.dataSource, i, v = this.grid.view;
17336         var s = this.selections;
17337         s.each(function(r){
17338             if((i = ds.indexOfId(r.id)) != -1){
17339                 v.onRowSelect(i);
17340             }else{
17341                 s.remove(r);
17342             }
17343         });
17344     },
17345
17346     // private
17347     onRemove : function(v, index, r){
17348         this.selections.remove(r);
17349     },
17350
17351     // private
17352     onRowUpdated : function(v, index, r){
17353         if(this.isSelected(r)){
17354             v.onRowSelect(index);
17355         }
17356     },
17357
17358     /**
17359      * Select records.
17360      * @param {Array} records The records to select
17361      * @param {Boolean} keepExisting (optional) True to keep existing selections
17362      */
17363     selectRecords : function(records, keepExisting){
17364         if(!keepExisting){
17365             this.clearSelections();
17366         }
17367         var ds = this.grid.dataSource;
17368         for(var i = 0, len = records.length; i < len; i++){
17369             this.selectRow(ds.indexOf(records[i]), true);
17370         }
17371     },
17372
17373     /**
17374      * Gets the number of selected rows.
17375      * @return {Number}
17376      */
17377     getCount : function(){
17378         return this.selections.length;
17379     },
17380
17381     /**
17382      * Selects the first row in the grid.
17383      */
17384     selectFirstRow : function(){
17385         this.selectRow(0);
17386     },
17387
17388     /**
17389      * Select the last row.
17390      * @param {Boolean} keepExisting (optional) True to keep existing selections
17391      */
17392     selectLastRow : function(keepExisting){
17393         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
17394     },
17395
17396     /**
17397      * Selects the row immediately following the last selected row.
17398      * @param {Boolean} keepExisting (optional) True to keep existing selections
17399      */
17400     selectNext : function(keepExisting){
17401         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
17402             this.selectRow(this.last+1, keepExisting);
17403             this.grid.getView().focusRow(this.last);
17404         }
17405     },
17406
17407     /**
17408      * Selects the row that precedes the last selected row.
17409      * @param {Boolean} keepExisting (optional) True to keep existing selections
17410      */
17411     selectPrevious : function(keepExisting){
17412         if(this.last){
17413             this.selectRow(this.last-1, keepExisting);
17414             this.grid.getView().focusRow(this.last);
17415         }
17416     },
17417
17418     /**
17419      * Returns the selected records
17420      * @return {Array} Array of selected records
17421      */
17422     getSelections : function(){
17423         return [].concat(this.selections.items);
17424     },
17425
17426     /**
17427      * Returns the first selected record.
17428      * @return {Record}
17429      */
17430     getSelected : function(){
17431         return this.selections.itemAt(0);
17432     },
17433
17434
17435     /**
17436      * Clears all selections.
17437      */
17438     clearSelections : function(fast){
17439         if(this.locked) return;
17440         if(fast !== true){
17441             var ds = this.grid.dataSource;
17442             var s = this.selections;
17443             s.each(function(r){
17444                 this.deselectRow(ds.indexOfId(r.id));
17445             }, this);
17446             s.clear();
17447         }else{
17448             this.selections.clear();
17449         }
17450         this.last = false;
17451     },
17452
17453
17454     /**
17455      * Selects all rows.
17456      */
17457     selectAll : function(){
17458         if(this.locked) return;
17459         this.selections.clear();
17460         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
17461             this.selectRow(i, true);
17462         }
17463     },
17464
17465     /**
17466      * Returns True if there is a selection.
17467      * @return {Boolean}
17468      */
17469     hasSelection : function(){
17470         return this.selections.length > 0;
17471     },
17472
17473     /**
17474      * Returns True if the specified row is selected.
17475      * @param {Number/Record} record The record or index of the record to check
17476      * @return {Boolean}
17477      */
17478     isSelected : function(index){
17479         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
17480         return (r && this.selections.key(r.id) ? true : false);
17481     },
17482
17483     /**
17484      * Returns True if the specified record id is selected.
17485      * @param {String} id The id of record to check
17486      * @return {Boolean}
17487      */
17488     isIdSelected : function(id){
17489         return (this.selections.key(id) ? true : false);
17490     },
17491
17492     // private
17493     handleMouseDown : function(e, t){
17494         var view = this.grid.getView(), rowIndex;
17495         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
17496             return;
17497         };
17498         if(e.shiftKey && this.last !== false){
17499             var last = this.last;
17500             this.selectRange(last, rowIndex, e.ctrlKey);
17501             this.last = last; // reset the last
17502             view.focusRow(rowIndex);
17503         }else{
17504             var isSelected = this.isSelected(rowIndex);
17505             if(e.button !== 0 && isSelected){
17506                 view.focusRow(rowIndex);
17507             }else if(e.ctrlKey && isSelected){
17508                 this.deselectRow(rowIndex);
17509             }else if(!isSelected){
17510                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
17511                 view.focusRow(rowIndex);
17512             }
17513         }
17514         this.fireEvent("afterselectionchange", this);
17515     },
17516     // private
17517     handleDragableRowClick :  function(grid, rowIndex, e) 
17518     {
17519         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
17520             this.selectRow(rowIndex, false);
17521             grid.view.focusRow(rowIndex);
17522              this.fireEvent("afterselectionchange", this);
17523         }
17524     },
17525     
17526     /**
17527      * Selects multiple rows.
17528      * @param {Array} rows Array of the indexes of the row to select
17529      * @param {Boolean} keepExisting (optional) True to keep existing selections
17530      */
17531     selectRows : function(rows, keepExisting){
17532         if(!keepExisting){
17533             this.clearSelections();
17534         }
17535         for(var i = 0, len = rows.length; i < len; i++){
17536             this.selectRow(rows[i], true);
17537         }
17538     },
17539
17540     /**
17541      * Selects a range of rows. All rows in between startRow and endRow are also selected.
17542      * @param {Number} startRow The index of the first row in the range
17543      * @param {Number} endRow The index of the last row in the range
17544      * @param {Boolean} keepExisting (optional) True to retain existing selections
17545      */
17546     selectRange : function(startRow, endRow, keepExisting){
17547         if(this.locked) return;
17548         if(!keepExisting){
17549             this.clearSelections();
17550         }
17551         if(startRow <= endRow){
17552             for(var i = startRow; i <= endRow; i++){
17553                 this.selectRow(i, true);
17554             }
17555         }else{
17556             for(var i = startRow; i >= endRow; i--){
17557                 this.selectRow(i, true);
17558             }
17559         }
17560     },
17561
17562     /**
17563      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
17564      * @param {Number} startRow The index of the first row in the range
17565      * @param {Number} endRow The index of the last row in the range
17566      */
17567     deselectRange : function(startRow, endRow, preventViewNotify){
17568         if(this.locked) return;
17569         for(var i = startRow; i <= endRow; i++){
17570             this.deselectRow(i, preventViewNotify);
17571         }
17572     },
17573
17574     /**
17575      * Selects a row.
17576      * @param {Number} row The index of the row to select
17577      * @param {Boolean} keepExisting (optional) True to keep existing selections
17578      */
17579     selectRow : function(index, keepExisting, preventViewNotify){
17580         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
17581         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
17582             if(!keepExisting || this.singleSelect){
17583                 this.clearSelections();
17584             }
17585             var r = this.grid.dataSource.getAt(index);
17586             this.selections.add(r);
17587             this.last = this.lastActive = index;
17588             if(!preventViewNotify){
17589                 this.grid.getView().onRowSelect(index);
17590             }
17591             this.fireEvent("rowselect", this, index, r);
17592             this.fireEvent("selectionchange", this);
17593         }
17594     },
17595
17596     /**
17597      * Deselects a row.
17598      * @param {Number} row The index of the row to deselect
17599      */
17600     deselectRow : function(index, preventViewNotify){
17601         if(this.locked) return;
17602         if(this.last == index){
17603             this.last = false;
17604         }
17605         if(this.lastActive == index){
17606             this.lastActive = false;
17607         }
17608         var r = this.grid.dataSource.getAt(index);
17609         this.selections.remove(r);
17610         if(!preventViewNotify){
17611             this.grid.getView().onRowDeselect(index);
17612         }
17613         this.fireEvent("rowdeselect", this, index);
17614         this.fireEvent("selectionchange", this);
17615     },
17616
17617     // private
17618     restoreLast : function(){
17619         if(this._last){
17620             this.last = this._last;
17621         }
17622     },
17623
17624     // private
17625     acceptsNav : function(row, col, cm){
17626         return !cm.isHidden(col) && cm.isCellEditable(col, row);
17627     },
17628
17629     // private
17630     onEditorKey : function(field, e){
17631         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
17632         if(k == e.TAB){
17633             e.stopEvent();
17634             ed.completeEdit();
17635             if(e.shiftKey){
17636                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
17637             }else{
17638                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
17639             }
17640         }else if(k == e.ENTER && !e.ctrlKey){
17641             e.stopEvent();
17642             ed.completeEdit();
17643             if(e.shiftKey){
17644                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
17645             }else{
17646                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
17647             }
17648         }else if(k == e.ESC){
17649             ed.cancelEdit();
17650         }
17651         if(newCell){
17652             g.startEditing(newCell[0], newCell[1]);
17653         }
17654     }
17655 });/*
17656  * Based on:
17657  * Ext JS Library 1.1.1
17658  * Copyright(c) 2006-2007, Ext JS, LLC.
17659  *
17660  * Originally Released Under LGPL - original licence link has changed is not relivant.
17661  *
17662  * Fork - LGPL
17663  * <script type="text/javascript">
17664  */
17665  
17666 /**
17667  * @class Roo.bootstrap.PagingToolbar
17668  * @extends Roo.Row
17669  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
17670  * @constructor
17671  * Create a new PagingToolbar
17672  * @param {Object} config The config object
17673  */
17674 Roo.bootstrap.PagingToolbar = function(config)
17675 {
17676     // old args format still supported... - xtype is prefered..
17677         // created from xtype...
17678     var ds = config.dataSource;
17679     this.toolbarItems = [];
17680     if (config.items) {
17681         this.toolbarItems = config.items;
17682 //        config.items = [];
17683     }
17684     
17685     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
17686     this.ds = ds;
17687     this.cursor = 0;
17688     if (ds) { 
17689         this.bind(ds);
17690     }
17691     
17692     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
17693     
17694 };
17695
17696 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
17697     /**
17698      * @cfg {Roo.data.Store} dataSource
17699      * The underlying data store providing the paged data
17700      */
17701     /**
17702      * @cfg {String/HTMLElement/Element} container
17703      * container The id or element that will contain the toolbar
17704      */
17705     /**
17706      * @cfg {Boolean} displayInfo
17707      * True to display the displayMsg (defaults to false)
17708      */
17709     /**
17710      * @cfg {Number} pageSize
17711      * The number of records to display per page (defaults to 20)
17712      */
17713     pageSize: 20,
17714     /**
17715      * @cfg {String} displayMsg
17716      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
17717      */
17718     displayMsg : 'Displaying {0} - {1} of {2}',
17719     /**
17720      * @cfg {String} emptyMsg
17721      * The message to display when no records are found (defaults to "No data to display")
17722      */
17723     emptyMsg : 'No data to display',
17724     /**
17725      * Customizable piece of the default paging text (defaults to "Page")
17726      * @type String
17727      */
17728     beforePageText : "Page",
17729     /**
17730      * Customizable piece of the default paging text (defaults to "of %0")
17731      * @type String
17732      */
17733     afterPageText : "of {0}",
17734     /**
17735      * Customizable piece of the default paging text (defaults to "First Page")
17736      * @type String
17737      */
17738     firstText : "First Page",
17739     /**
17740      * Customizable piece of the default paging text (defaults to "Previous Page")
17741      * @type String
17742      */
17743     prevText : "Previous Page",
17744     /**
17745      * Customizable piece of the default paging text (defaults to "Next Page")
17746      * @type String
17747      */
17748     nextText : "Next Page",
17749     /**
17750      * Customizable piece of the default paging text (defaults to "Last Page")
17751      * @type String
17752      */
17753     lastText : "Last Page",
17754     /**
17755      * Customizable piece of the default paging text (defaults to "Refresh")
17756      * @type String
17757      */
17758     refreshText : "Refresh",
17759
17760     buttons : false,
17761     // private
17762     onRender : function(ct, position) 
17763     {
17764         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
17765         this.navgroup.parentId = this.id;
17766         this.navgroup.onRender(this.el, null);
17767         // add the buttons to the navgroup
17768         
17769         if(this.displayInfo){
17770             Roo.log(this.el.select('ul.navbar-nav',true).first());
17771             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
17772             this.displayEl = this.el.select('.x-paging-info', true).first();
17773 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
17774 //            this.displayEl = navel.el.select('span',true).first();
17775         }
17776         
17777         var _this = this;
17778         
17779         if(this.buttons){
17780             Roo.each(_this.buttons, function(e){
17781                Roo.factory(e).onRender(_this.el, null);
17782             });
17783         }
17784             
17785         Roo.each(_this.toolbarItems, function(e) {
17786             _this.navgroup.addItem(e);
17787         });
17788         
17789         this.first = this.navgroup.addItem({
17790             tooltip: this.firstText,
17791             cls: "prev",
17792             icon : 'fa fa-backward',
17793             disabled: true,
17794             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
17795         });
17796         
17797         this.prev =  this.navgroup.addItem({
17798             tooltip: this.prevText,
17799             cls: "prev",
17800             icon : 'fa fa-step-backward',
17801             disabled: true,
17802             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
17803         });
17804     //this.addSeparator();
17805         
17806         
17807         var field = this.navgroup.addItem( {
17808             tagtype : 'span',
17809             cls : 'x-paging-position',
17810             
17811             html : this.beforePageText  +
17812                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
17813                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
17814          } ); //?? escaped?
17815         
17816         this.field = field.el.select('input', true).first();
17817         this.field.on("keydown", this.onPagingKeydown, this);
17818         this.field.on("focus", function(){this.dom.select();});
17819     
17820     
17821         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
17822         //this.field.setHeight(18);
17823         //this.addSeparator();
17824         this.next = this.navgroup.addItem({
17825             tooltip: this.nextText,
17826             cls: "next",
17827             html : ' <i class="fa fa-step-forward">',
17828             disabled: true,
17829             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
17830         });
17831         this.last = this.navgroup.addItem({
17832             tooltip: this.lastText,
17833             icon : 'fa fa-forward',
17834             cls: "next",
17835             disabled: true,
17836             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
17837         });
17838     //this.addSeparator();
17839         this.loading = this.navgroup.addItem({
17840             tooltip: this.refreshText,
17841             icon: 'fa fa-refresh',
17842             
17843             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
17844         });
17845
17846     },
17847
17848     // private
17849     updateInfo : function(){
17850         if(this.displayEl){
17851             var count = this.ds.getCount();
17852             var msg = count == 0 ?
17853                 this.emptyMsg :
17854                 String.format(
17855                     this.displayMsg,
17856                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
17857                 );
17858             this.displayEl.update(msg);
17859         }
17860     },
17861
17862     // private
17863     onLoad : function(ds, r, o){
17864        this.cursor = o.params ? o.params.start : 0;
17865        var d = this.getPageData(),
17866             ap = d.activePage,
17867             ps = d.pages;
17868         
17869        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
17870        this.field.dom.value = ap;
17871        this.first.setDisabled(ap == 1);
17872        this.prev.setDisabled(ap == 1);
17873        this.next.setDisabled(ap == ps);
17874        this.last.setDisabled(ap == ps);
17875        this.loading.enable();
17876        this.updateInfo();
17877     },
17878
17879     // private
17880     getPageData : function(){
17881         var total = this.ds.getTotalCount();
17882         return {
17883             total : total,
17884             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
17885             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
17886         };
17887     },
17888
17889     // private
17890     onLoadError : function(){
17891         this.loading.enable();
17892     },
17893
17894     // private
17895     onPagingKeydown : function(e){
17896         var k = e.getKey();
17897         var d = this.getPageData();
17898         if(k == e.RETURN){
17899             var v = this.field.dom.value, pageNum;
17900             if(!v || isNaN(pageNum = parseInt(v, 10))){
17901                 this.field.dom.value = d.activePage;
17902                 return;
17903             }
17904             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
17905             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
17906             e.stopEvent();
17907         }
17908         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))
17909         {
17910           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
17911           this.field.dom.value = pageNum;
17912           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
17913           e.stopEvent();
17914         }
17915         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
17916         {
17917           var v = this.field.dom.value, pageNum; 
17918           var increment = (e.shiftKey) ? 10 : 1;
17919           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
17920             increment *= -1;
17921           if(!v || isNaN(pageNum = parseInt(v, 10))) {
17922             this.field.dom.value = d.activePage;
17923             return;
17924           }
17925           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
17926           {
17927             this.field.dom.value = parseInt(v, 10) + increment;
17928             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
17929             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
17930           }
17931           e.stopEvent();
17932         }
17933     },
17934
17935     // private
17936     beforeLoad : function(){
17937         if(this.loading){
17938             this.loading.disable();
17939         }
17940     },
17941
17942     // private
17943     onClick : function(which){
17944         var ds = this.ds;
17945         if (!ds) {
17946             return;
17947         }
17948         switch(which){
17949             case "first":
17950                 ds.load({params:{start: 0, limit: this.pageSize}});
17951             break;
17952             case "prev":
17953                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
17954             break;
17955             case "next":
17956                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
17957             break;
17958             case "last":
17959                 var total = ds.getTotalCount();
17960                 var extra = total % this.pageSize;
17961                 var lastStart = extra ? (total - extra) : total-this.pageSize;
17962                 ds.load({params:{start: lastStart, limit: this.pageSize}});
17963             break;
17964             case "refresh":
17965                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
17966             break;
17967         }
17968     },
17969
17970     /**
17971      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
17972      * @param {Roo.data.Store} store The data store to unbind
17973      */
17974     unbind : function(ds){
17975         ds.un("beforeload", this.beforeLoad, this);
17976         ds.un("load", this.onLoad, this);
17977         ds.un("loadexception", this.onLoadError, this);
17978         ds.un("remove", this.updateInfo, this);
17979         ds.un("add", this.updateInfo, this);
17980         this.ds = undefined;
17981     },
17982
17983     /**
17984      * Binds the paging toolbar to the specified {@link Roo.data.Store}
17985      * @param {Roo.data.Store} store The data store to bind
17986      */
17987     bind : function(ds){
17988         ds.on("beforeload", this.beforeLoad, this);
17989         ds.on("load", this.onLoad, this);
17990         ds.on("loadexception", this.onLoadError, this);
17991         ds.on("remove", this.updateInfo, this);
17992         ds.on("add", this.updateInfo, this);
17993         this.ds = ds;
17994     }
17995 });/*
17996  * - LGPL
17997  *
17998  * element
17999  * 
18000  */
18001
18002 /**
18003  * @class Roo.bootstrap.MessageBar
18004  * @extends Roo.bootstrap.Component
18005  * Bootstrap MessageBar class
18006  * @cfg {String} html contents of the MessageBar
18007  * @cfg {String} weight (info | success | warning | danger) default info
18008  * @cfg {String} beforeClass insert the bar before the given class
18009  * @cfg {Boolean} closable (true | false) default false
18010  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
18011  * 
18012  * @constructor
18013  * Create a new Element
18014  * @param {Object} config The config object
18015  */
18016
18017 Roo.bootstrap.MessageBar = function(config){
18018     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
18019 };
18020
18021 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
18022     
18023     html: '',
18024     weight: 'info',
18025     closable: false,
18026     fixed: false,
18027     beforeClass: 'bootstrap-sticky-wrap',
18028     
18029     getAutoCreate : function(){
18030         
18031         var cfg = {
18032             tag: 'div',
18033             cls: 'alert alert-dismissable alert-' + this.weight,
18034             cn: [
18035                 {
18036                     tag: 'span',
18037                     cls: 'message',
18038                     html: this.html || ''
18039                 }
18040             ]
18041         }
18042         
18043         if(this.fixed){
18044             cfg.cls += ' alert-messages-fixed';
18045         }
18046         
18047         if(this.closable){
18048             cfg.cn.push({
18049                 tag: 'button',
18050                 cls: 'close',
18051                 html: 'x'
18052             });
18053         }
18054         
18055         return cfg;
18056     },
18057     
18058     onRender : function(ct, position)
18059     {
18060         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18061         
18062         if(!this.el){
18063             var cfg = Roo.apply({},  this.getAutoCreate());
18064             cfg.id = Roo.id();
18065             
18066             if (this.cls) {
18067                 cfg.cls += ' ' + this.cls;
18068             }
18069             if (this.style) {
18070                 cfg.style = this.style;
18071             }
18072             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
18073             
18074             this.el.setVisibilityMode(Roo.Element.DISPLAY);
18075         }
18076         
18077         this.el.select('>button.close').on('click', this.hide, this);
18078         
18079     },
18080     
18081     show : function()
18082     {
18083         if (!this.rendered) {
18084             this.render();
18085         }
18086         
18087         this.el.show();
18088         
18089         this.fireEvent('show', this);
18090         
18091     },
18092     
18093     hide : function()
18094     {
18095         if (!this.rendered) {
18096             this.render();
18097         }
18098         
18099         this.el.hide();
18100         
18101         this.fireEvent('hide', this);
18102     },
18103     
18104     update : function()
18105     {
18106 //        var e = this.el.dom.firstChild;
18107 //        
18108 //        if(this.closable){
18109 //            e = e.nextSibling;
18110 //        }
18111 //        
18112 //        e.data = this.html || '';
18113
18114         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
18115     }
18116    
18117 });
18118
18119  
18120
18121      /*
18122  * - LGPL
18123  *
18124  * Graph
18125  * 
18126  */
18127
18128
18129 /**
18130  * @class Roo.bootstrap.Graph
18131  * @extends Roo.bootstrap.Component
18132  * Bootstrap Graph class
18133 > Prameters
18134  -sm {number} sm 4
18135  -md {number} md 5
18136  @cfg {String} graphtype  bar | vbar | pie
18137  @cfg {number} g_x coodinator | centre x (pie)
18138  @cfg {number} g_y coodinator | centre y (pie)
18139  @cfg {number} g_r radius (pie)
18140  @cfg {number} g_height height of the chart (respected by all elements in the set)
18141  @cfg {number} g_width width of the chart (respected by all elements in the set)
18142  @cfg {Object} title The title of the chart
18143     
18144  -{Array}  values
18145  -opts (object) options for the chart 
18146      o {
18147      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
18148      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
18149      o vgutter (number)
18150      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.
18151      o stacked (boolean) whether or not to tread values as in a stacked bar chart
18152      o to
18153      o stretch (boolean)
18154      o }
18155  -opts (object) options for the pie
18156      o{
18157      o cut
18158      o startAngle (number)
18159      o endAngle (number)
18160      } 
18161  *
18162  * @constructor
18163  * Create a new Input
18164  * @param {Object} config The config object
18165  */
18166
18167 Roo.bootstrap.Graph = function(config){
18168     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
18169     
18170     this.addEvents({
18171         // img events
18172         /**
18173          * @event click
18174          * The img click event for the img.
18175          * @param {Roo.EventObject} e
18176          */
18177         "click" : true
18178     });
18179 };
18180
18181 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
18182     
18183     sm: 4,
18184     md: 5,
18185     graphtype: 'bar',
18186     g_height: 250,
18187     g_width: 400,
18188     g_x: 50,
18189     g_y: 50,
18190     g_r: 30,
18191     opts:{
18192         //g_colors: this.colors,
18193         g_type: 'soft',
18194         g_gutter: '20%'
18195
18196     },
18197     title : false,
18198
18199     getAutoCreate : function(){
18200         
18201         var cfg = {
18202             tag: 'div',
18203             html : null
18204         }
18205         
18206         
18207         return  cfg;
18208     },
18209
18210     onRender : function(ct,position){
18211         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
18212         this.raphael = Raphael(this.el.dom);
18213
18214                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
18215                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
18216                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
18217                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
18218                 /*
18219                 r.text(160, 10, "Single Series Chart").attr(txtattr);
18220                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
18221                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
18222                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
18223                 
18224                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
18225                 r.barchart(330, 10, 300, 220, data1);
18226                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
18227                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
18228                 */
18229                 
18230                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
18231                 // r.barchart(30, 30, 560, 250,  xdata, {
18232                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
18233                 //     axis : "0 0 1 1",
18234                 //     axisxlabels :  xdata
18235                 //     //yvalues : cols,
18236                    
18237                 // });
18238 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
18239 //        
18240 //        this.load(null,xdata,{
18241 //                axis : "0 0 1 1",
18242 //                axisxlabels :  xdata
18243 //                });
18244
18245     },
18246
18247     load : function(graphtype,xdata,opts){
18248         this.raphael.clear();
18249         if(!graphtype) {
18250             graphtype = this.graphtype;
18251         }
18252         if(!opts){
18253             opts = this.opts;
18254         }
18255         var r = this.raphael,
18256             fin = function () {
18257                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
18258             },
18259             fout = function () {
18260                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
18261             };
18262
18263         switch(graphtype){
18264             case 'bar':
18265                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
18266                 break;
18267             case 'hbar':
18268                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
18269                 break;
18270             case 'pie':
18271                 opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
18272                 href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
18273             
18274                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts);
18275                 
18276                 break;
18277
18278         }
18279         
18280         if(this.title){
18281             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
18282         }
18283         
18284     },
18285     
18286     setTitle: function(o)
18287     {
18288         this.title = o;
18289     },
18290     
18291     initEvents: function() {
18292         
18293         if(!this.href){
18294             this.el.on('click', this.onClick, this);
18295         }
18296     },
18297     
18298     onClick : function(e)
18299     {
18300         Roo.log('img onclick');
18301         this.fireEvent('click', this, e);
18302     }
18303    
18304 });
18305
18306  
18307 /*
18308  * - LGPL
18309  *
18310  * numberBox
18311  * 
18312  */
18313 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
18314
18315 /**
18316  * @class Roo.bootstrap.dash.NumberBox
18317  * @extends Roo.bootstrap.Component
18318  * Bootstrap NumberBox class
18319  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
18320  * @cfg {String} headline Box headline
18321  * @cfg {String} content Box content
18322  * @cfg {String} icon Box icon
18323  * @cfg {String} footer Footer text
18324  * @cfg {String} fhref Footer href
18325  * 
18326  * @constructor
18327  * Create a new NumberBox
18328  * @param {Object} config The config object
18329  */
18330
18331
18332 Roo.bootstrap.dash.NumberBox = function(config){
18333     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
18334     
18335 };
18336
18337 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
18338     
18339     bgcolor : 'aqua',
18340     headline : '',
18341     content : '',
18342     icon : '',
18343     footer : '',
18344     fhref : '',
18345     ficon : '',
18346     
18347     getAutoCreate : function(){
18348         
18349         var cfg = {
18350             tag : 'div',
18351             cls : 'small-box bg-' + this.bgcolor,
18352             cn : [
18353                 {
18354                     tag : 'div',
18355                     cls : 'inner',
18356                     cn :[
18357                         {
18358                             tag : 'h3',
18359                             cls : 'roo-headline',
18360                             html : this.headline
18361                         },
18362                         {
18363                             tag : 'p',
18364                             cls : 'roo-content',
18365                             html : this.content
18366                         }
18367                     ]
18368                 }
18369             ]
18370         }
18371         
18372         if(this.icon){
18373             cfg.cn.push({
18374                 tag : 'div',
18375                 cls : 'icon',
18376                 cn :[
18377                     {
18378                         tag : 'i',
18379                         cls : 'ion ' + this.icon
18380                     }
18381                 ]
18382             });
18383         }
18384         
18385         if(this.footer){
18386             var footer = {
18387                 tag : 'a',
18388                 cls : 'small-box-footer',
18389                 href : this.fhref || '#',
18390                 html : this.footer
18391             };
18392             
18393             cfg.cn.push(footer);
18394             
18395         }
18396         
18397         return  cfg;
18398     },
18399
18400     onRender : function(ct,position){
18401         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
18402
18403
18404        
18405                 
18406     },
18407
18408     setHeadline: function (value)
18409     {
18410         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
18411     },
18412
18413
18414     initEvents: function() 
18415     {   
18416         
18417     }
18418     
18419 });
18420
18421  
18422 /*
18423  * - LGPL
18424  *
18425  * TabBox
18426  * 
18427  */
18428 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
18429
18430 /**
18431  * @class Roo.bootstrap.dash.TabBox
18432  * @extends Roo.bootstrap.Component
18433  * Bootstrap TabBox class
18434  * @cfg {String} title Title of the TabBox
18435  * @cfg {String} icon Icon of the TabBox
18436  * 
18437  * @constructor
18438  * Create a new TabBox
18439  * @param {Object} config The config object
18440  */
18441
18442
18443 Roo.bootstrap.dash.TabBox = function(config){
18444     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
18445     
18446 };
18447
18448 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
18449
18450     title : '',
18451     icon : false,
18452     
18453     getChildContainer : function()
18454     {
18455         return this.el.select('.tab-content', true).first();
18456     },
18457     
18458     getAutoCreate : function(){
18459         
18460         var header = {
18461             tag: 'li',
18462             cls: 'pull-left header',
18463             html: this.title,
18464             cn : []
18465         };
18466         
18467         if(this.icon){
18468             header.cn.push({
18469                 tag: 'i',
18470                 cls: 'fa ' + this.icon
18471             });
18472         }
18473         
18474         
18475         var cfg = {
18476             tag: 'div',
18477             cls: 'nav-tabs-custom',
18478             cn: [
18479                 {
18480                     tag: 'ul',
18481                     cls: 'nav nav-tabs pull-right',
18482                     cn: [
18483                         header
18484                     ]
18485                 },
18486                 {
18487                     tag: 'div',
18488                     cls: 'tab-content no-padding',
18489                     cn: []
18490                 }
18491             ]
18492         }
18493
18494         return  cfg;
18495     },
18496     
18497     setTitle : function(value)
18498     {
18499         this.el.select('.header', true).first().dom.innerHTML = value;
18500     }
18501     
18502 });
18503
18504  
18505 /*
18506  * - LGPL
18507  *
18508  * Tab pane
18509  * 
18510  */
18511 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
18512 /**
18513  * @class Roo.bootstrap.TabPane
18514  * @extends Roo.bootstrap.Component
18515  * Bootstrap TabPane class
18516  * @cfg {Boolean} active (false | true) Default false
18517
18518  * 
18519  * @constructor
18520  * Create a new TabPane
18521  * @param {Object} config The config object
18522  */
18523
18524 Roo.bootstrap.dash.TabPane = function(config){
18525     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
18526     
18527 };
18528
18529 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
18530     
18531     active : false,
18532 //    
18533 //    getBox : function()
18534 //    {
18535 //        return this.el.findParent('.nav-tabs-custom', false, true);
18536 //    },
18537     
18538     getAutoCreate : function() 
18539     {
18540         var cfg = {
18541             tag: 'div',
18542             cls: 'tab-pane'
18543         }
18544         
18545         if(this.active){
18546             cfg.cls += ' active';
18547         }
18548         
18549         return cfg;
18550     }
18551     
18552     
18553 });
18554
18555  
18556
18557
18558  /*
18559  * - LGPL
18560  *
18561  * menu
18562  * 
18563  */
18564 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18565
18566 /**
18567  * @class Roo.bootstrap.menu.Menu
18568  * @extends Roo.bootstrap.Component
18569  * Bootstrap Menu class - container for Menu
18570  * @cfg {String} html Text of the menu
18571  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
18572  * @cfg {String} icon Font awesome icon
18573  * @cfg {String} pos Menu align to (top | bottom) default bottom
18574  * 
18575  * 
18576  * @constructor
18577  * Create a new Menu
18578  * @param {Object} config The config object
18579  */
18580
18581
18582 Roo.bootstrap.menu.Menu = function(config){
18583     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
18584     
18585     this.addEvents({
18586         /**
18587          * @event beforeshow
18588          * Fires before this menu is displayed
18589          * @param {Roo.bootstrap.menu.Menu} this
18590          */
18591         beforeshow : true,
18592         /**
18593          * @event beforehide
18594          * Fires before this menu is hidden
18595          * @param {Roo.bootstrap.menu.Menu} this
18596          */
18597         beforehide : true,
18598         /**
18599          * @event show
18600          * Fires after this menu is displayed
18601          * @param {Roo.bootstrap.menu.Menu} this
18602          */
18603         show : true,
18604         /**
18605          * @event hide
18606          * Fires after this menu is hidden
18607          * @param {Roo.bootstrap.menu.Menu} this
18608          */
18609         hide : true,
18610         /**
18611          * @event click
18612          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
18613          * @param {Roo.bootstrap.menu.Menu} this
18614          * @param {Roo.EventObject} e
18615          */
18616         click : true
18617     });
18618     
18619 };
18620
18621 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
18622     
18623     submenu : false,
18624     html : '',
18625     weight : 'default',
18626     icon : false,
18627     pos : 'bottom',
18628     
18629     
18630     getChildContainer : function() {
18631         if(this.isSubMenu()){
18632             return this.el;
18633         }
18634         
18635         return this.el.select('ul.dropdown-menu', true).first();  
18636     },
18637     
18638     getAutoCreate : function()
18639     {
18640         var text = [
18641             {
18642                 tag : 'span',
18643                 cls : 'roo-menu-text',
18644                 html : this.html
18645             }
18646         ];
18647         
18648         if(this.icon){
18649             text.unshift({
18650                 tag : 'i',
18651                 cls : 'fa ' + this.icon
18652             })
18653         }
18654         
18655         
18656         var cfg = {
18657             tag : 'div',
18658             cls : 'btn-group',
18659             cn : [
18660                 {
18661                     tag : 'button',
18662                     cls : 'dropdown-button btn btn-' + this.weight,
18663                     cn : text
18664                 },
18665                 {
18666                     tag : 'button',
18667                     cls : 'dropdown-toggle btn btn-' + this.weight,
18668                     cn : [
18669                         {
18670                             tag : 'span',
18671                             cls : 'caret'
18672                         }
18673                     ]
18674                 },
18675                 {
18676                     tag : 'ul',
18677                     cls : 'dropdown-menu'
18678                 }
18679             ]
18680             
18681         };
18682         
18683         if(this.pos == 'top'){
18684             cfg.cls += ' dropup';
18685         }
18686         
18687         if(this.isSubMenu()){
18688             cfg = {
18689                 tag : 'ul',
18690                 cls : 'dropdown-menu'
18691             }
18692         }
18693         
18694         return cfg;
18695     },
18696     
18697     isSubMenu : function()
18698     {
18699         if(this.parent() instanceof Roo.bootstrap.menu.Item){
18700             return true;
18701         }
18702         
18703         return false;
18704         
18705     },
18706     
18707     initEvents : function() 
18708     {
18709         
18710         if(this.isSubMenu()){
18711             return;
18712         }
18713         
18714         this.hidden = true;
18715         
18716         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
18717         this.triggerEl.on('click', this.onTriggerPress, this);
18718         
18719         this.buttonEl = this.el.select('button.dropdown-button', true).first();
18720         this.buttonEl.on('click', this.onClick, this);
18721         
18722     },
18723     
18724     onClick : function(e)
18725     {
18726         this.fireEvent("click", this, e);
18727     },
18728     
18729     onTriggerPress  : function(e)
18730     {   
18731         if (this.isVisible()) {
18732             this.hide();
18733         } else {
18734             this.show();
18735         }
18736     },
18737     
18738     isVisible : function(){
18739         return !this.hidden;
18740     },
18741     
18742     show : function()
18743     {
18744         this.fireEvent("beforeshow", this);
18745         
18746         this.hidden = false;
18747         this.el.addClass('open');
18748         
18749         Roo.get(document).on("mouseup", this.onMouseUp, this);
18750         
18751         this.fireEvent("show", this);
18752         
18753     },
18754     
18755     hide : function()
18756     {
18757         this.fireEvent("beforehide", this);
18758         
18759         this.hidden = true;
18760         this.el.removeClass('open');
18761         
18762         Roo.get(document).un("mouseup", this.onMouseUp);
18763         
18764         this.fireEvent("hide", this);
18765     },
18766     
18767     onMouseUp : function()
18768     {
18769         this.hide();
18770     }
18771     
18772 });
18773
18774  
18775  /*
18776  * - LGPL
18777  *
18778  * menu item
18779  * 
18780  */
18781 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18782
18783 /**
18784  * @class Roo.bootstrap.menu.Item
18785  * @extends Roo.bootstrap.Component
18786  * Bootstrap MenuItem class
18787  * @cfg {Boolean} submenu (true | false) default false
18788  * @cfg {String} html text of the item
18789  * @cfg {String} href the link
18790  * @cfg {Boolean} disable (true | false) default false
18791  * @cfg {Boolean} preventDefault (true | false) default true
18792  * @cfg {String} icon Font awesome icon
18793  * @cfg {String} pos Submenu align to (left | right) default right 
18794  * 
18795  * 
18796  * @constructor
18797  * Create a new Item
18798  * @param {Object} config The config object
18799  */
18800
18801
18802 Roo.bootstrap.menu.Item = function(config){
18803     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
18804     this.addEvents({
18805         /**
18806          * @event mouseover
18807          * Fires when the mouse is hovering over this menu
18808          * @param {Roo.bootstrap.menu.Item} this
18809          * @param {Roo.EventObject} e
18810          */
18811         mouseover : true,
18812         /**
18813          * @event mouseout
18814          * Fires when the mouse exits this menu
18815          * @param {Roo.bootstrap.menu.Item} this
18816          * @param {Roo.EventObject} e
18817          */
18818         mouseout : true,
18819         // raw events
18820         /**
18821          * @event click
18822          * The raw click event for the entire grid.
18823          * @param {Roo.EventObject} e
18824          */
18825         click : true
18826     });
18827 };
18828
18829 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
18830     
18831     submenu : false,
18832     href : '',
18833     html : '',
18834     preventDefault: true,
18835     disable : false,
18836     icon : false,
18837     pos : 'right',
18838     
18839     getAutoCreate : function()
18840     {
18841         var text = [
18842             {
18843                 tag : 'span',
18844                 cls : 'roo-menu-item-text',
18845                 html : this.html
18846             }
18847         ];
18848         
18849         if(this.icon){
18850             text.unshift({
18851                 tag : 'i',
18852                 cls : 'fa ' + this.icon
18853             })
18854         }
18855         
18856         var cfg = {
18857             tag : 'li',
18858             cn : [
18859                 {
18860                     tag : 'a',
18861                     href : this.href || '#',
18862                     cn : text
18863                 }
18864             ]
18865         };
18866         
18867         if(this.disable){
18868             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
18869         }
18870         
18871         if(this.submenu){
18872             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
18873             
18874             if(this.pos == 'left'){
18875                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
18876             }
18877         }
18878         
18879         return cfg;
18880     },
18881     
18882     initEvents : function() 
18883     {
18884         this.el.on('mouseover', this.onMouseOver, this);
18885         this.el.on('mouseout', this.onMouseOut, this);
18886         
18887         this.el.select('a', true).first().on('click', this.onClick, this);
18888         
18889     },
18890     
18891     onClick : function(e)
18892     {
18893         if(this.preventDefault){
18894             e.preventDefault();
18895         }
18896         
18897         this.fireEvent("click", this, e);
18898     },
18899     
18900     onMouseOver : function(e)
18901     {
18902         this.fireEvent("mouseover", this, e);
18903     },
18904     
18905     onMouseOut : function(e)
18906     {
18907         this.fireEvent("mouseout", this, e);
18908     }
18909 });
18910
18911  
18912
18913  /*
18914  * - LGPL
18915  *
18916  * menu separator
18917  * 
18918  */
18919 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18920
18921 /**
18922  * @class Roo.bootstrap.menu.Separator
18923  * @extends Roo.bootstrap.Component
18924  * Bootstrap Separator class
18925  * 
18926  * @constructor
18927  * Create a new Separator
18928  * @param {Object} config The config object
18929  */
18930
18931
18932 Roo.bootstrap.menu.Separator = function(config){
18933     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
18934 };
18935
18936 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
18937     
18938     getAutoCreate : function(){
18939         var cfg = {
18940             tag : 'li',
18941             cls: 'divider'
18942         };
18943         
18944         return cfg;
18945     }
18946    
18947 });
18948
18949  
18950
18951