roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr](true));
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         /*
249         if (typeof (tree.menu) != 'undefined') {
250             tree.menu.parentType = cn.xtype;
251             tree.menu.triggerEl = cn.el;
252             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253             
254         }
255         */
256         if (!tree.items || !tree.items.length) {
257             cn.items = nitems;
258             return cn;
259         }
260         var items = tree.items;
261         delete tree.items;
262         
263         //Roo.log(items.length);
264             // add the items..
265         for(var i =0;i < items.length;i++) {
266             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
267         }
268         
269         cn.items = nitems;
270         
271         return cn;
272     }
273     
274     
275     
276     
277 });
278
279  /*
280  * - LGPL
281  *
282  * Body
283  * 
284  */
285
286 /**
287  * @class Roo.bootstrap.Body
288  * @extends Roo.bootstrap.Component
289  * Bootstrap Body class
290  * 
291  * @constructor
292  * Create a new body
293  * @param {Object} config The config object
294  */
295
296 Roo.bootstrap.Body = function(config){
297     Roo.bootstrap.Body.superclass.constructor.call(this, config);
298     this.el = Roo.get(document.body);
299     if (this.cls && this.cls.length) {
300         Roo.get(document.body).addClass(this.cls);
301     }
302 };
303
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
305       
306         autoCreate : {
307         cls: 'container'
308     },
309     onRender : function(ct, position)
310     {
311        /* Roo.log("Roo.bootstrap.Body - onRender");
312         if (this.cls && this.cls.length) {
313             Roo.get(document.body).addClass(this.cls);
314         }
315         // style??? xttr???
316         */
317     }
318     
319     
320  
321    
322 });
323
324  /*
325  * - LGPL
326  *
327  * button group
328  * 
329  */
330
331
332 /**
333  * @class Roo.bootstrap.ButtonGroup
334  * @extends Roo.bootstrap.Component
335  * Bootstrap ButtonGroup class
336  * @cfg {String} size lg | sm | xs (default empty normal)
337  * @cfg {String} align vertical | justified  (default none)
338  * @cfg {String} direction up | down (default down)
339  * @cfg {Boolean} toolbar false | true
340  * @cfg {Boolean} btn true | false
341  * 
342  * 
343  * @constructor
344  * Create a new Input
345  * @param {Object} config The config object
346  */
347
348 Roo.bootstrap.ButtonGroup = function(config){
349     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
350 };
351
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
353     
354     size: '',
355     align: '',
356     direction: '',
357     toolbar: false,
358     btn: true,
359
360     getAutoCreate : function(){
361         var cfg = {
362             cls: 'btn-group',
363             html : null
364         }
365         
366         cfg.html = this.html || cfg.html;
367         
368         if (this.toolbar) {
369             cfg = {
370                 cls: 'btn-toolbar',
371                 html: null
372             }
373             
374             return cfg;
375         }
376         
377         if (['vertical','justified'].indexOf(this.align)!==-1) {
378             cfg.cls = 'btn-group-' + this.align;
379             
380             if (this.align == 'justified') {
381                 console.log(this.items);
382             }
383         }
384         
385         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386             cfg.cls += ' btn-group-' + this.size;
387         }
388         
389         if (this.direction == 'up') {
390             cfg.cls += ' dropup' ;
391         }
392         
393         return cfg;
394     }
395    
396 });
397
398  /*
399  * - LGPL
400  *
401  * button
402  * 
403  */
404
405 /**
406  * @class Roo.bootstrap.Button
407  * @extends Roo.bootstrap.Component
408  * Bootstrap Button class
409  * @cfg {String} html The button content
410  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411  * @cfg {String} size empty | lg | sm | xs
412  * @cfg {String} tag empty | a | input | submit
413  * @cfg {String} href empty or href
414  * @cfg {Boolean} disabled false | true
415  * @cfg {Boolean} isClose false | true
416  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
417  * @cfg {String} badge text for badge
418  * @cfg {String} theme default (or empty) | glow
419  * @cfg {Boolean} inverse false | true
420  * @cfg {Boolean} toggle false | true
421  * @cfg {String} ontext text for on toggle state
422  * @cfg {String} offtext text for off toggle state
423  * @cfg {Boolean} defaulton true | false
424  * @cfg {Boolean} preventDefault (true | false) default true
425  * @cfg {Boolean} removeClass true | false remove the standard class..
426  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
427  * 
428  * @constructor
429  * Create a new button
430  * @param {Object} config The config object
431  */
432
433
434 Roo.bootstrap.Button = function(config){
435     Roo.bootstrap.Button.superclass.constructor.call(this, config);
436     this.addEvents({
437         // raw events
438         /**
439          * @event click
440          * When a butotn is pressed
441          * @param {Roo.EventObject} e
442          */
443         "click" : true,
444          /**
445          * @event toggle
446          * After the button has been toggles
447          * @param {Roo.EventObject} e
448          * @param {boolean} pressed (also available as button.pressed)
449          */
450         "toggle" : true
451     });
452 };
453
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
455     html: false,
456     active: false,
457     weight: '',
458     size: '',
459     tag: 'button',
460     href: '',
461     disabled: false,
462     isClose: false,
463     glyphicon: '',
464     badge: '',
465     theme: 'default',
466     inverse: false,
467     
468     toggle: false,
469     ontext: 'ON',
470     offtext: 'OFF',
471     defaulton: true,
472     preventDefault: true,
473     removeClass: false,
474     name: false,
475     target: false,
476     
477     
478     pressed : null,
479      
480     
481     getAutoCreate : function(){
482         
483         var cfg = {
484             tag : 'button',
485             cls : 'roo-button',
486             html: ''
487         };
488         
489         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
491             this.tag = 'button';
492         } else {
493             cfg.tag = this.tag;
494         }
495         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496         
497         if (this.toggle == true) {
498             cfg={
499                 tag: 'div',
500                 cls: 'slider-frame roo-button',
501                 cn: [
502                     {
503                         tag: 'span',
504                         'data-on-text':'ON',
505                         'data-off-text':'OFF',
506                         cls: 'slider-button',
507                         html: this.offtext
508                     }
509                 ]
510             };
511             
512             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513                 cfg.cls += ' '+this.weight;
514             }
515             
516             return cfg;
517         }
518         
519         if (this.isClose) {
520             cfg.cls += ' close';
521             
522             cfg["aria-hidden"] = true;
523             
524             cfg.html = "&times;";
525             
526             return cfg;
527         }
528         
529          
530         if (this.theme==='default') {
531             cfg.cls = 'btn roo-button';
532             
533             //if (this.parentType != 'Navbar') {
534             this.weight = this.weight.length ?  this.weight : 'default';
535             //}
536             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537                 
538                 cfg.cls += ' btn-' + this.weight;
539             }
540         } else if (this.theme==='glow') {
541             
542             cfg.tag = 'a';
543             cfg.cls = 'btn-glow roo-button';
544             
545             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546                 
547                 cfg.cls += ' ' + this.weight;
548             }
549         }
550    
551         
552         if (this.inverse) {
553             this.cls += ' inverse';
554         }
555         
556         
557         if (this.active) {
558             cfg.cls += ' active';
559         }
560         
561         if (this.disabled) {
562             cfg.disabled = 'disabled';
563         }
564         
565         if (this.items) {
566             Roo.log('changing to ul' );
567             cfg.tag = 'ul';
568             this.glyphicon = 'caret';
569         }
570         
571         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572          
573         //gsRoo.log(this.parentType);
574         if (this.parentType === 'Navbar' && !this.parent().bar) {
575             Roo.log('changing to li?');
576             
577             cfg.tag = 'li';
578             
579             cfg.cls = '';
580             cfg.cn =  [{
581                 tag : 'a',
582                 cls : 'roo-button',
583                 html : this.html,
584                 href : this.href || '#'
585             }];
586             if (this.menu) {
587                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
588                 cfg.cls += ' dropdown';
589             }   
590             
591             delete cfg.html;
592             
593         }
594         
595        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
596         
597         if (this.glyphicon) {
598             cfg.html = ' ' + cfg.html;
599             
600             cfg.cn = [
601                 {
602                     tag: 'span',
603                     cls: 'glyphicon glyphicon-' + this.glyphicon
604                 }
605             ];
606         }
607         
608         if (this.badge) {
609             cfg.html += ' ';
610             
611             cfg.tag = 'a';
612             
613 //            cfg.cls='btn roo-button';
614             
615             cfg.href=this.href;
616             
617             var value = cfg.html;
618             
619             if(this.glyphicon){
620                 value = {
621                             tag: 'span',
622                             cls: 'glyphicon glyphicon-' + this.glyphicon,
623                             html: this.html
624                         };
625                 
626             }
627             
628             cfg.cn = [
629                 value,
630                 {
631                     tag: 'span',
632                     cls: 'badge',
633                     html: this.badge
634                 }
635             ];
636             
637             cfg.html='';
638         }
639         
640         if (this.menu) {
641             cfg.cls += ' dropdown';
642             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
643         }
644         
645         if (cfg.tag !== 'a' && this.href !== '') {
646             throw "Tag must be a to set href.";
647         } else if (this.href.length > 0) {
648             cfg.href = this.href;
649         }
650         
651         if(this.removeClass){
652             cfg.cls = '';
653         }
654         
655         if(this.target){
656             cfg.target = this.target;
657         }
658         
659         return cfg;
660     },
661     initEvents: function() {
662        // Roo.log('init events?');
663 //        Roo.log(this.el.dom);
664         // add the menu...
665         
666         if (typeof (this.menu) != 'undefined') {
667             this.menu.parentType = this.xtype;
668             this.menu.triggerEl = this.el;
669             this.addxtype(Roo.apply({}, this.menu));
670         }
671
672
673        if (this.el.hasClass('roo-button')) {
674             this.el.on('click', this.onClick, this);
675        } else {
676             this.el.select('.roo-button').on('click', this.onClick, this);
677        }
678        
679        if(this.removeClass){
680            this.el.on('click', this.onClick, this);
681        }
682        
683        this.el.enableDisplayMode();
684         
685     },
686     onClick : function(e)
687     {
688         if (this.disabled) {
689             return;
690         }
691         
692         Roo.log('button on click ');
693         if(this.preventDefault){
694             e.preventDefault();
695         }
696         if (this.pressed === true || this.pressed === false) {
697             this.pressed = !this.pressed;
698             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699             this.fireEvent('toggle', this, e, this.pressed);
700         }
701         
702         
703         this.fireEvent('click', this, e);
704     },
705     
706     /**
707      * Enables this button
708      */
709     enable : function()
710     {
711         this.disabled = false;
712         this.el.removeClass('disabled');
713     },
714     
715     /**
716      * Disable this button
717      */
718     disable : function()
719     {
720         this.disabled = true;
721         this.el.addClass('disabled');
722     },
723      /**
724      * sets the active state on/off, 
725      * @param {Boolean} state (optional) Force a particular state
726      */
727     setActive : function(v) {
728         
729         this.el[v ? 'addClass' : 'removeClass']('active');
730     },
731      /**
732      * toggles the current active state 
733      */
734     toggleActive : function()
735     {
736        var active = this.el.hasClass('active');
737        this.setActive(!active);
738        
739         
740     },
741     setText : function(str)
742     {
743         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
744     },
745     getText : function()
746     {
747         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
748     },
749     hide: function() {
750        
751      
752         this.el.hide();   
753     },
754     show: function() {
755        
756         this.el.show();   
757     }
758     
759     
760 });
761
762  /*
763  * - LGPL
764  *
765  * column
766  * 
767  */
768
769 /**
770  * @class Roo.bootstrap.Column
771  * @extends Roo.bootstrap.Component
772  * Bootstrap Column class
773  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
775  * @cfg {Number} md colspan out of 12 for computer-sized screens
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
777  * @cfg {String} html content of column.
778  * 
779  * @constructor
780  * Create a new Column
781  * @param {Object} config The config object
782  */
783
784 Roo.bootstrap.Column = function(config){
785     Roo.bootstrap.Column.superclass.constructor.call(this, config);
786 };
787
788 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
789     
790     xs: null,
791     sm: null,
792     md: null,
793     lg: null,
794     html: '',
795     offset: 0,
796     
797     getAutoCreate : function(){
798         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
799         
800         cfg = {
801             tag: 'div',
802             cls: 'column'
803         };
804         
805         var settings=this;
806         ['xs','sm','md','lg'].map(function(size){
807             if (settings[size]) {
808                 cfg.cls += ' col-' + size + '-' + settings[size];
809             }
810         });
811         if (this.html.length) {
812             cfg.html = this.html;
813         }
814         
815         return cfg;
816     }
817    
818 });
819
820  
821
822  /*
823  * - LGPL
824  *
825  * page container.
826  * 
827  */
828
829
830 /**
831  * @class Roo.bootstrap.Container
832  * @extends Roo.bootstrap.Component
833  * Bootstrap Container class
834  * @cfg {Boolean} jumbotron is it a jumbotron element
835  * @cfg {String} html content of element
836  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
837  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
838  * @cfg {String} header content of header (for panel)
839  * @cfg {String} footer content of footer (for panel)
840  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
841  * @cfg {String} tag (header|aside|section) type of HTML tag.
842
843  *     
844  * @constructor
845  * Create a new Container
846  * @param {Object} config The config object
847  */
848
849 Roo.bootstrap.Container = function(config){
850     Roo.bootstrap.Container.superclass.constructor.call(this, config);
851 };
852
853 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
854     
855     jumbotron : false,
856     well: '',
857     panel : '',
858     header: '',
859     footer : '',
860     sticky: '',
861     tag : false,
862   
863      
864     getChildContainer : function() {
865         
866         if(!this.el){
867             return false;
868         }
869         
870         if (this.panel.length) {
871             return this.el.select('.panel-body',true).first();
872         }
873         
874         return this.el;
875     },
876     
877     
878     getAutoCreate : function(){
879         
880         var cfg = {
881             tag : this.tag || 'div',
882             html : '',
883             cls : ''
884         };
885         if (this.jumbotron) {
886             cfg.cls = 'jumbotron';
887         }
888         // - this is applied by the parent..
889         //if (this.cls) {
890         //    cfg.cls = this.cls + '';
891         //}
892         
893         if (this.sticky.length) {
894             
895             var bd = Roo.get(document.body);
896             if (!bd.hasClass('bootstrap-sticky')) {
897                 bd.addClass('bootstrap-sticky');
898                 Roo.select('html',true).setStyle('height', '100%');
899             }
900              
901             cfg.cls += 'bootstrap-sticky-' + this.sticky;
902         }
903         
904         
905         if (this.well.length) {
906             switch (this.well) {
907                 case 'lg':
908                 case 'sm':
909                     cfg.cls +=' well well-' +this.well;
910                     break;
911                 default:
912                     cfg.cls +=' well';
913                     break;
914             }
915         }
916         
917         var body = cfg;
918         
919         if (this.panel.length) {
920             cfg.cls += ' panel panel-' + this.panel;
921             cfg.cn = [];
922             if (this.header.length) {
923                 cfg.cn.push({
924                     
925                     cls : 'panel-heading',
926                     cn : [{
927                         tag: 'h3',
928                         cls : 'panel-title',
929                         html : this.header
930                     }]
931                     
932                 });
933             }
934             body = false;
935             cfg.cn.push({
936                 cls : 'panel-body',
937                 html : this.html
938             });
939             
940             
941             if (this.footer.length) {
942                 cfg.cn.push({
943                     cls : 'panel-footer',
944                     html : this.footer
945                     
946                 });
947             }
948             
949         }
950         
951         if (body) {
952             body.html = this.html || cfg.html;
953         }
954         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
955             cfg.cls =  'container';
956         }
957         
958         return cfg;
959     },
960     
961     titleEl : function()
962     {
963         if(!this.el || !this.panel.length || !this.header.length){
964             return;
965         }
966         
967         return this.el.select('.panel-title',true).first();
968     },
969     
970     setTitle : function(v)
971     {
972         var titleEl = this.titleEl();
973         
974         if(!titleEl){
975             return;
976         }
977         
978         titleEl.dom.innerHTML = v;
979     },
980     
981     getTitle : function()
982     {
983         
984         var titleEl = this.titleEl();
985         
986         if(!titleEl){
987             return '';
988         }
989         
990         return titleEl.dom.innerHTML;
991     }
992    
993 });
994
995  /*
996  * - LGPL
997  *
998  * image
999  * 
1000  */
1001
1002
1003 /**
1004  * @class Roo.bootstrap.Img
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Img class
1007  * @cfg {Boolean} imgResponsive false | true
1008  * @cfg {String} border rounded | circle | thumbnail
1009  * @cfg {String} src image source
1010  * @cfg {String} alt image alternative text
1011  * @cfg {String} href a tag href
1012  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1013  * 
1014  * @constructor
1015  * Create a new Input
1016  * @param {Object} config The config object
1017  */
1018
1019 Roo.bootstrap.Img = function(config){
1020     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1021     
1022     this.addEvents({
1023         // img events
1024         /**
1025          * @event click
1026          * The img click event for the img.
1027          * @param {Roo.EventObject} e
1028          */
1029         "click" : true
1030     });
1031 };
1032
1033 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1034     
1035     imgResponsive: true,
1036     border: '',
1037     src: '',
1038     href: false,
1039     target: false,
1040
1041     getAutoCreate : function(){
1042         
1043         var cfg = {
1044             tag: 'img',
1045             cls: (this.imgResponsive) ? 'img-responsive' : '',
1046             html : null
1047         }
1048         
1049         cfg.html = this.html || cfg.html;
1050         
1051         cfg.src = this.src || cfg.src;
1052         
1053         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1054             cfg.cls += ' img-' + this.border;
1055         }
1056         
1057         if(this.alt){
1058             cfg.alt = this.alt;
1059         }
1060         
1061         if(this.href){
1062             var a = {
1063                 tag: 'a',
1064                 href: this.href,
1065                 cn: [
1066                     cfg
1067                 ]
1068             }
1069             
1070             if(this.target){
1071                 a.target = this.target;
1072             }
1073             
1074         }
1075         
1076         
1077         return (this.href) ? a : cfg;
1078     },
1079     
1080     initEvents: function() {
1081         
1082         if(!this.href){
1083             this.el.on('click', this.onClick, this);
1084         }
1085     },
1086     
1087     onClick : function(e)
1088     {
1089         Roo.log('img onclick');
1090         this.fireEvent('click', this, e);
1091     }
1092    
1093 });
1094
1095  /*
1096  * - LGPL
1097  *
1098  * image
1099  * 
1100  */
1101
1102
1103 /**
1104  * @class Roo.bootstrap.Link
1105  * @extends Roo.bootstrap.Component
1106  * Bootstrap Link Class
1107  * @cfg {String} alt image alternative text
1108  * @cfg {String} href a tag href
1109  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1110  * @cfg {String} html the content of the link.
1111  * @cfg {Boolean} preventDefault (true | false) default false
1112
1113  * 
1114  * @constructor
1115  * Create a new Input
1116  * @param {Object} config The config object
1117  */
1118
1119 Roo.bootstrap.Link = function(config){
1120     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1121     
1122     this.addEvents({
1123         // img events
1124         /**
1125          * @event click
1126          * The img click event for the img.
1127          * @param {Roo.EventObject} e
1128          */
1129         "click" : true
1130     });
1131 };
1132
1133 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1134     
1135     href: false,
1136     target: false,
1137     preventDefault: false,
1138
1139     getAutoCreate : function(){
1140         
1141         var cfg = {
1142             tag: 'a',
1143             html : this.html || 'html-missing'
1144         }
1145         
1146         
1147         if(this.alt){
1148             cfg.alt = this.alt;
1149         }
1150         cfg.href = this.href || '#';
1151         if(this.target){
1152             cfg.target = this.target;
1153         }
1154         
1155         return cfg;
1156     },
1157     
1158     initEvents: function() {
1159         
1160         if(!this.href || this.preventDefault){
1161             this.el.on('click', this.onClick, this);
1162         }
1163     },
1164     
1165     onClick : function(e)
1166     {
1167         if(this.preventDefault){
1168             e.preventDefault();
1169         }
1170         //Roo.log('img onclick');
1171         this.fireEvent('click', this, e);
1172     }
1173    
1174 });
1175
1176  /*
1177  * - LGPL
1178  *
1179  * header
1180  * 
1181  */
1182
1183 /**
1184  * @class Roo.bootstrap.Header
1185  * @extends Roo.bootstrap.Component
1186  * Bootstrap Header class
1187  * @cfg {String} html content of header
1188  * @cfg {Number} level (1|2|3|4|5|6) default 1
1189  * 
1190  * @constructor
1191  * Create a new Header
1192  * @param {Object} config The config object
1193  */
1194
1195
1196 Roo.bootstrap.Header  = function(config){
1197     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1198 };
1199
1200 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1201     
1202     //href : false,
1203     html : false,
1204     level : 1,
1205     
1206     
1207     
1208     getAutoCreate : function(){
1209         
1210         var cfg = {
1211             tag: 'h' + (1 *this.level),
1212             html: this.html || 'fill in html'
1213         } ;
1214         
1215         return cfg;
1216     }
1217    
1218 });
1219
1220  
1221
1222  /*
1223  * Based on:
1224  * Ext JS Library 1.1.1
1225  * Copyright(c) 2006-2007, Ext JS, LLC.
1226  *
1227  * Originally Released Under LGPL - original licence link has changed is not relivant.
1228  *
1229  * Fork - LGPL
1230  * <script type="text/javascript">
1231  */
1232  
1233 /**
1234  * @class Roo.bootstrap.MenuMgr
1235  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1236  * @singleton
1237  */
1238 Roo.bootstrap.MenuMgr = function(){
1239    var menus, active, groups = {}, attached = false, lastShow = new Date();
1240
1241    // private - called when first menu is created
1242    function init(){
1243        menus = {};
1244        active = new Roo.util.MixedCollection();
1245        Roo.get(document).addKeyListener(27, function(){
1246            if(active.length > 0){
1247                hideAll();
1248            }
1249        });
1250    }
1251
1252    // private
1253    function hideAll(){
1254        if(active && active.length > 0){
1255            var c = active.clone();
1256            c.each(function(m){
1257                m.hide();
1258            });
1259        }
1260    }
1261
1262    // private
1263    function onHide(m){
1264        active.remove(m);
1265        if(active.length < 1){
1266            Roo.get(document).un("mouseup", onMouseDown);
1267             
1268            attached = false;
1269        }
1270    }
1271
1272    // private
1273    function onShow(m){
1274        var last = active.last();
1275        lastShow = new Date();
1276        active.add(m);
1277        if(!attached){
1278           Roo.get(document).on("mouseup", onMouseDown);
1279            
1280            attached = true;
1281        }
1282        if(m.parentMenu){
1283           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1284           m.parentMenu.activeChild = m;
1285        }else if(last && last.isVisible()){
1286           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1287        }
1288    }
1289
1290    // private
1291    function onBeforeHide(m){
1292        if(m.activeChild){
1293            m.activeChild.hide();
1294        }
1295        if(m.autoHideTimer){
1296            clearTimeout(m.autoHideTimer);
1297            delete m.autoHideTimer;
1298        }
1299    }
1300
1301    // private
1302    function onBeforeShow(m){
1303        var pm = m.parentMenu;
1304        if(!pm && !m.allowOtherMenus){
1305            hideAll();
1306        }else if(pm && pm.activeChild && active != m){
1307            pm.activeChild.hide();
1308        }
1309    }
1310
1311    // private
1312    function onMouseDown(e){
1313         Roo.log("on MouseDown");
1314         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1315            hideAll();
1316         }
1317         
1318         
1319    }
1320
1321    // private
1322    function onBeforeCheck(mi, state){
1323        if(state){
1324            var g = groups[mi.group];
1325            for(var i = 0, l = g.length; i < l; i++){
1326                if(g[i] != mi){
1327                    g[i].setChecked(false);
1328                }
1329            }
1330        }
1331    }
1332
1333    return {
1334
1335        /**
1336         * Hides all menus that are currently visible
1337         */
1338        hideAll : function(){
1339             hideAll();  
1340        },
1341
1342        // private
1343        register : function(menu){
1344            if(!menus){
1345                init();
1346            }
1347            menus[menu.id] = menu;
1348            menu.on("beforehide", onBeforeHide);
1349            menu.on("hide", onHide);
1350            menu.on("beforeshow", onBeforeShow);
1351            menu.on("show", onShow);
1352            var g = menu.group;
1353            if(g && menu.events["checkchange"]){
1354                if(!groups[g]){
1355                    groups[g] = [];
1356                }
1357                groups[g].push(menu);
1358                menu.on("checkchange", onCheck);
1359            }
1360        },
1361
1362         /**
1363          * Returns a {@link Roo.menu.Menu} object
1364          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1365          * be used to generate and return a new Menu instance.
1366          */
1367        get : function(menu){
1368            if(typeof menu == "string"){ // menu id
1369                return menus[menu];
1370            }else if(menu.events){  // menu instance
1371                return menu;
1372            }
1373            /*else if(typeof menu.length == 'number'){ // array of menu items?
1374                return new Roo.bootstrap.Menu({items:menu});
1375            }else{ // otherwise, must be a config
1376                return new Roo.bootstrap.Menu(menu);
1377            }
1378            */
1379            return false;
1380        },
1381
1382        // private
1383        unregister : function(menu){
1384            delete menus[menu.id];
1385            menu.un("beforehide", onBeforeHide);
1386            menu.un("hide", onHide);
1387            menu.un("beforeshow", onBeforeShow);
1388            menu.un("show", onShow);
1389            var g = menu.group;
1390            if(g && menu.events["checkchange"]){
1391                groups[g].remove(menu);
1392                menu.un("checkchange", onCheck);
1393            }
1394        },
1395
1396        // private
1397        registerCheckable : function(menuItem){
1398            var g = menuItem.group;
1399            if(g){
1400                if(!groups[g]){
1401                    groups[g] = [];
1402                }
1403                groups[g].push(menuItem);
1404                menuItem.on("beforecheckchange", onBeforeCheck);
1405            }
1406        },
1407
1408        // private
1409        unregisterCheckable : function(menuItem){
1410            var g = menuItem.group;
1411            if(g){
1412                groups[g].remove(menuItem);
1413                menuItem.un("beforecheckchange", onBeforeCheck);
1414            }
1415        }
1416    };
1417 }();/*
1418  * - LGPL
1419  *
1420  * menu
1421  * 
1422  */
1423
1424 /**
1425  * @class Roo.bootstrap.Menu
1426  * @extends Roo.bootstrap.Component
1427  * Bootstrap Menu class - container for MenuItems
1428  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1429  * 
1430  * @constructor
1431  * Create a new Menu
1432  * @param {Object} config The config object
1433  */
1434
1435
1436 Roo.bootstrap.Menu = function(config){
1437     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1438     if (this.registerMenu) {
1439         Roo.bootstrap.MenuMgr.register(this);
1440     }
1441     this.addEvents({
1442         /**
1443          * @event beforeshow
1444          * Fires before this menu is displayed
1445          * @param {Roo.menu.Menu} this
1446          */
1447         beforeshow : true,
1448         /**
1449          * @event beforehide
1450          * Fires before this menu is hidden
1451          * @param {Roo.menu.Menu} this
1452          */
1453         beforehide : true,
1454         /**
1455          * @event show
1456          * Fires after this menu is displayed
1457          * @param {Roo.menu.Menu} this
1458          */
1459         show : true,
1460         /**
1461          * @event hide
1462          * Fires after this menu is hidden
1463          * @param {Roo.menu.Menu} this
1464          */
1465         hide : true,
1466         /**
1467          * @event click
1468          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1469          * @param {Roo.menu.Menu} this
1470          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1471          * @param {Roo.EventObject} e
1472          */
1473         click : true,
1474         /**
1475          * @event mouseover
1476          * Fires when the mouse is hovering over this menu
1477          * @param {Roo.menu.Menu} this
1478          * @param {Roo.EventObject} e
1479          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1480          */
1481         mouseover : true,
1482         /**
1483          * @event mouseout
1484          * Fires when the mouse exits this menu
1485          * @param {Roo.menu.Menu} this
1486          * @param {Roo.EventObject} e
1487          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1488          */
1489         mouseout : true,
1490         /**
1491          * @event itemclick
1492          * Fires when a menu item contained in this menu is clicked
1493          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1494          * @param {Roo.EventObject} e
1495          */
1496         itemclick: true
1497     });
1498     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1499 };
1500
1501 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1502     
1503    /// html : false,
1504     //align : '',
1505     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1506     type: false,
1507     /**
1508      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1509      */
1510     registerMenu : true,
1511     
1512     menuItems :false, // stores the menu items..
1513     
1514     hidden:true,
1515     
1516     parentMenu : false,
1517     
1518     getChildContainer : function() {
1519         return this.el;  
1520     },
1521     
1522     getAutoCreate : function(){
1523          
1524         //if (['right'].indexOf(this.align)!==-1) {
1525         //    cfg.cn[1].cls += ' pull-right'
1526         //}
1527         
1528         
1529         var cfg = {
1530             tag : 'ul',
1531             cls : 'dropdown-menu' ,
1532             style : 'z-index:1000'
1533             
1534         }
1535         
1536         if (this.type === 'submenu') {
1537             cfg.cls = 'submenu active';
1538         }
1539         if (this.type === 'treeview') {
1540             cfg.cls = 'treeview-menu';
1541         }
1542         
1543         return cfg;
1544     },
1545     initEvents : function() {
1546         
1547        // Roo.log("ADD event");
1548        // Roo.log(this.triggerEl.dom);
1549         this.triggerEl.on('click', this.onTriggerPress, this);
1550         this.triggerEl.addClass('dropdown-toggle');
1551         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1552
1553         this.el.on("mouseover", this.onMouseOver, this);
1554         this.el.on("mouseout", this.onMouseOut, this);
1555         
1556         
1557     },
1558     findTargetItem : function(e){
1559         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1560         if(!t){
1561             return false;
1562         }
1563         //Roo.log(t);         Roo.log(t.id);
1564         if(t && t.id){
1565             //Roo.log(this.menuitems);
1566             return this.menuitems.get(t.id);
1567             
1568             //return this.items.get(t.menuItemId);
1569         }
1570         
1571         return false;
1572     },
1573     onClick : function(e){
1574         Roo.log("menu.onClick");
1575         var t = this.findTargetItem(e);
1576         if(!t){
1577             return;
1578         }
1579         Roo.log(e);
1580         /*
1581         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1582             if(t == this.activeItem && t.shouldDeactivate(e)){
1583                 this.activeItem.deactivate();
1584                 delete this.activeItem;
1585                 return;
1586             }
1587             if(t.canActivate){
1588                 this.setActiveItem(t, true);
1589             }
1590             return;
1591             
1592             
1593         }
1594         */
1595         Roo.log('pass click event');
1596         
1597         t.onClick(e);
1598         
1599         this.fireEvent("click", this, t, e);
1600         
1601         this.hide();
1602     },
1603      onMouseOver : function(e){
1604         var t  = this.findTargetItem(e);
1605         //Roo.log(t);
1606         //if(t){
1607         //    if(t.canActivate && !t.disabled){
1608         //        this.setActiveItem(t, true);
1609         //    }
1610         //}
1611         
1612         this.fireEvent("mouseover", this, e, t);
1613     },
1614     isVisible : function(){
1615         return !this.hidden;
1616     },
1617      onMouseOut : function(e){
1618         var t  = this.findTargetItem(e);
1619         
1620         //if(t ){
1621         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1622         //        this.activeItem.deactivate();
1623         //        delete this.activeItem;
1624         //    }
1625         //}
1626         this.fireEvent("mouseout", this, e, t);
1627     },
1628     
1629     
1630     /**
1631      * Displays this menu relative to another element
1632      * @param {String/HTMLElement/Roo.Element} element The element to align to
1633      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1634      * the element (defaults to this.defaultAlign)
1635      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1636      */
1637     show : function(el, pos, parentMenu){
1638         this.parentMenu = parentMenu;
1639         if(!this.el){
1640             this.render();
1641         }
1642         this.fireEvent("beforeshow", this);
1643         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1644     },
1645      /**
1646      * Displays this menu at a specific xy position
1647      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1648      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1649      */
1650     showAt : function(xy, parentMenu, /* private: */_e){
1651         this.parentMenu = parentMenu;
1652         if(!this.el){
1653             this.render();
1654         }
1655         if(_e !== false){
1656             this.fireEvent("beforeshow", this);
1657             
1658             //xy = this.el.adjustForConstraints(xy);
1659         }
1660         //this.el.setXY(xy);
1661         //this.el.show();
1662         this.hideMenuItems();
1663         this.hidden = false;
1664         this.triggerEl.addClass('open');
1665         this.focus();
1666         this.fireEvent("show", this);
1667     },
1668     
1669     focus : function(){
1670         return;
1671         if(!this.hidden){
1672             this.doFocus.defer(50, this);
1673         }
1674     },
1675
1676     doFocus : function(){
1677         if(!this.hidden){
1678             this.focusEl.focus();
1679         }
1680     },
1681
1682     /**
1683      * Hides this menu and optionally all parent menus
1684      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1685      */
1686     hide : function(deep){
1687         
1688         this.hideMenuItems();
1689         if(this.el && this.isVisible()){
1690             this.fireEvent("beforehide", this);
1691             if(this.activeItem){
1692                 this.activeItem.deactivate();
1693                 this.activeItem = null;
1694             }
1695             this.triggerEl.removeClass('open');;
1696             this.hidden = true;
1697             this.fireEvent("hide", this);
1698         }
1699         if(deep === true && this.parentMenu){
1700             this.parentMenu.hide(true);
1701         }
1702     },
1703     
1704     onTriggerPress  : function(e)
1705     {
1706         
1707         Roo.log('trigger press');
1708         //Roo.log(e.getTarget());
1709        // Roo.log(this.triggerEl.dom);
1710         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1711             return;
1712         }
1713         if (this.isVisible()) {
1714             Roo.log('hide');
1715             this.hide();
1716         } else {
1717             this.show(this.triggerEl, false, false);
1718         }
1719         
1720         
1721     },
1722     
1723          
1724        
1725     
1726     hideMenuItems : function()
1727     {
1728         //$(backdrop).remove()
1729         Roo.select('.open',true).each(function(aa) {
1730             
1731             aa.removeClass('open');
1732           //var parent = getParent($(this))
1733           //var relatedTarget = { relatedTarget: this }
1734           
1735            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1736           //if (e.isDefaultPrevented()) return
1737            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1738         })
1739     },
1740     addxtypeChild : function (tree, cntr) {
1741         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1742           
1743         this.menuitems.add(comp);
1744         return comp;
1745
1746     },
1747     getEl : function()
1748     {
1749         Roo.log(this.el);
1750         return this.el;
1751     }
1752 });
1753
1754  
1755  /*
1756  * - LGPL
1757  *
1758  * menu item
1759  * 
1760  */
1761
1762
1763 /**
1764  * @class Roo.bootstrap.MenuItem
1765  * @extends Roo.bootstrap.Component
1766  * Bootstrap MenuItem class
1767  * @cfg {String} html the menu label
1768  * @cfg {String} href the link
1769  * @cfg {Boolean} preventDefault (true | false) default true
1770  * 
1771  * 
1772  * @constructor
1773  * Create a new MenuItem
1774  * @param {Object} config The config object
1775  */
1776
1777
1778 Roo.bootstrap.MenuItem = function(config){
1779     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1780     this.addEvents({
1781         // raw events
1782         /**
1783          * @event click
1784          * The raw click event for the entire grid.
1785          * @param {Roo.EventObject} e
1786          */
1787         "click" : true
1788     });
1789 };
1790
1791 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1792     
1793     href : false,
1794     html : false,
1795     preventDefault: true,
1796     
1797     getAutoCreate : function(){
1798         var cfg= {
1799             tag: 'li',
1800             cls: 'dropdown-menu-item',
1801             cn: [
1802                     {
1803                         tag : 'a',
1804                         href : '#',
1805                         html : 'Link'
1806                     }
1807                 ]
1808         };
1809         if (this.parent().type == 'treeview') {
1810             cfg.cls = 'treeview-menu';
1811         }
1812         
1813         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1814         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1815         return cfg;
1816     },
1817     
1818     initEvents: function() {
1819         
1820         //this.el.select('a').on('click', this.onClick, this);
1821         
1822     },
1823     onClick : function(e)
1824     {
1825         Roo.log('item on click ');
1826         //if(this.preventDefault){
1827         //    e.preventDefault();
1828         //}
1829         //this.parent().hideMenuItems();
1830         
1831         this.fireEvent('click', this, e);
1832     },
1833     getEl : function()
1834     {
1835         return this.el;
1836     }
1837 });
1838
1839  
1840
1841  /*
1842  * - LGPL
1843  *
1844  * menu separator
1845  * 
1846  */
1847
1848
1849 /**
1850  * @class Roo.bootstrap.MenuSeparator
1851  * @extends Roo.bootstrap.Component
1852  * Bootstrap MenuSeparator class
1853  * 
1854  * @constructor
1855  * Create a new MenuItem
1856  * @param {Object} config The config object
1857  */
1858
1859
1860 Roo.bootstrap.MenuSeparator = function(config){
1861     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1862 };
1863
1864 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1865     
1866     getAutoCreate : function(){
1867         var cfg = {
1868             cls: 'divider',
1869             tag : 'li'
1870         };
1871         
1872         return cfg;
1873     }
1874    
1875 });
1876
1877  
1878
1879  
1880 /*
1881 <div class="modal fade">
1882   <div class="modal-dialog">
1883     <div class="modal-content">
1884       <div class="modal-header">
1885         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1886         <h4 class="modal-title">Modal title</h4>
1887       </div>
1888       <div class="modal-body">
1889         <p>One fine body&hellip;</p>
1890       </div>
1891       <div class="modal-footer">
1892         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1893         <button type="button" class="btn btn-primary">Save changes</button>
1894       </div>
1895     </div><!-- /.modal-content -->
1896   </div><!-- /.modal-dialog -->
1897 </div><!-- /.modal -->
1898 */
1899 /*
1900  * - LGPL
1901  *
1902  * page contgainer.
1903  * 
1904  */
1905
1906 /**
1907  * @class Roo.bootstrap.Modal
1908  * @extends Roo.bootstrap.Component
1909  * Bootstrap Modal class
1910  * @cfg {String} title Title of dialog
1911  * @cfg {Boolean} specificTitle (true|false) default false
1912  * @cfg {Array} buttons Array of buttons or standard button set..
1913  * @cfg {String} buttonPosition (left|right|center) default right
1914  * 
1915  * @constructor
1916  * Create a new Modal Dialog
1917  * @param {Object} config The config object
1918  */
1919
1920 Roo.bootstrap.Modal = function(config){
1921     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1922     this.addEvents({
1923         // raw events
1924         /**
1925          * @event btnclick
1926          * The raw btnclick event for the button
1927          * @param {Roo.EventObject} e
1928          */
1929         "btnclick" : true
1930     });
1931     this.buttons = this.buttons || [];
1932 };
1933
1934 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1935     
1936     title : 'test dialog',
1937    
1938     buttons : false,
1939     
1940     // set on load...
1941     body:  false,
1942     
1943     specificTitle: false,
1944     
1945     buttonPosition: 'right',
1946     
1947     onRender : function(ct, position)
1948     {
1949         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1950      
1951         if(!this.el){
1952             var cfg = Roo.apply({},  this.getAutoCreate());
1953             cfg.id = Roo.id();
1954             //if(!cfg.name){
1955             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1956             //}
1957             //if (!cfg.name.length) {
1958             //    delete cfg.name;
1959            // }
1960             if (this.cls) {
1961                 cfg.cls += ' ' + this.cls;
1962             }
1963             if (this.style) {
1964                 cfg.style = this.style;
1965             }
1966             this.el = Roo.get(document.body).createChild(cfg, position);
1967         }
1968         //var type = this.el.dom.type;
1969         
1970         if(this.tabIndex !== undefined){
1971             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1972         }
1973         
1974         
1975         
1976         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1977         this.maskEl.enableDisplayMode("block");
1978         this.maskEl.hide();
1979         //this.el.addClass("x-dlg-modal");
1980     
1981         if (this.buttons.length) {
1982             Roo.each(this.buttons, function(bb) {
1983                 b = Roo.apply({}, bb);
1984                 b.xns = b.xns || Roo.bootstrap;
1985                 b.xtype = b.xtype || 'Button';
1986                 if (typeof(b.listeners) == 'undefined') {
1987                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1988                 }
1989                 
1990                 var btn = Roo.factory(b);
1991                 
1992                 btn.onRender(this.el.select('.modal-footer div').first());
1993                 
1994             },this);
1995         }
1996         // render the children.
1997         var nitems = [];
1998         
1999         if(typeof(this.items) != 'undefined'){
2000             var items = this.items;
2001             delete this.items;
2002
2003             for(var i =0;i < items.length;i++) {
2004                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2005             }
2006         }
2007         
2008         this.items = nitems;
2009         
2010         this.body = this.el.select('.modal-body',true).first();
2011         this.close = this.el.select('.modal-header .close', true).first();
2012         this.footer = this.el.select('.modal-footer',true).first();
2013         this.initEvents();
2014         //this.el.addClass([this.fieldClass, this.cls]);
2015         
2016     },
2017     getAutoCreate : function(){
2018         
2019         
2020         var bdy = {
2021                 cls : 'modal-body',
2022                 html : this.html || ''
2023         };
2024         
2025         var title = {
2026             tag: 'h4',
2027             cls : 'modal-title',
2028             html : this.title
2029         };
2030         
2031         if(this.specificTitle){
2032             title = this.title;
2033         };
2034         
2035         return modal = {
2036             cls: "modal fade",
2037             style : 'display: none',
2038             cn : [
2039                 {
2040                     cls: "modal-dialog",
2041                     cn : [
2042                         {
2043                             cls : "modal-content",
2044                             cn : [
2045                                 {
2046                                     cls : 'modal-header',
2047                                     cn : [
2048                                         {
2049                                             tag: 'button',
2050                                             cls : 'close',
2051                                             html : '&times'
2052                                         },
2053                                         title
2054                                     ]
2055                                 },
2056                                 bdy,
2057                                 {
2058                                     cls : 'modal-footer',
2059                                     cn : [
2060                                         {
2061                                             tag: 'div',
2062                                             cls: 'btn-' + this.buttonPosition
2063                                         }
2064                                     ]
2065                                     
2066                                 }
2067                                 
2068                                 
2069                             ]
2070                             
2071                         }
2072                     ]
2073                         
2074                 }
2075             ]
2076             
2077             
2078         };
2079           
2080     },
2081     getChildContainer : function() {
2082          
2083          return this.el.select('.modal-body',true).first();
2084         
2085     },
2086     getButtonContainer : function() {
2087          return this.el.select('.modal-footer div',true).first();
2088         
2089     },
2090     initEvents : function()
2091     {
2092         this.el.select('.modal-header .close').on('click', this.hide, this);
2093 //        
2094 //        this.addxtype(this);
2095     },
2096     show : function() {
2097         
2098         if (!this.rendered) {
2099             this.render();
2100         }
2101        
2102         this.el.addClass('on');
2103         this.el.removeClass('fade');
2104         this.el.setStyle('display', 'block');
2105         Roo.get(document.body).addClass("x-body-masked");
2106         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2107         this.maskEl.show();
2108         this.el.setStyle('zIndex', '10001');
2109         this.fireEvent('show', this);
2110         
2111         
2112     },
2113     hide : function()
2114     {
2115         Roo.log('Modal hide?!');
2116         this.maskEl.hide();
2117         Roo.get(document.body).removeClass("x-body-masked");
2118         this.el.removeClass('on');
2119         this.el.addClass('fade');
2120         this.el.setStyle('display', 'none');
2121         this.fireEvent('hide', this);
2122     },
2123     
2124     addButton : function(str, cb)
2125     {
2126          
2127         
2128         var b = Roo.apply({}, { html : str } );
2129         b.xns = b.xns || Roo.bootstrap;
2130         b.xtype = b.xtype || 'Button';
2131         if (typeof(b.listeners) == 'undefined') {
2132             b.listeners = { click : cb.createDelegate(this)  };
2133         }
2134         
2135         var btn = Roo.factory(b);
2136            
2137         btn.onRender(this.el.select('.modal-footer div').first());
2138         
2139         return btn;   
2140        
2141     },
2142     
2143     setDefaultButton : function(btn)
2144     {
2145         //this.el.select('.modal-footer').()
2146     },
2147     resizeTo: function(w,h)
2148     {
2149         // skip..
2150     },
2151     setContentSize  : function(w, h)
2152     {
2153         
2154     },
2155     onButtonClick: function(btn,e)
2156     {
2157         //Roo.log([a,b,c]);
2158         this.fireEvent('btnclick', btn.name, e);
2159     },
2160     setTitle: function(str) {
2161         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2162         
2163     }
2164 });
2165
2166
2167 Roo.apply(Roo.bootstrap.Modal,  {
2168     /**
2169          * Button config that displays a single OK button
2170          * @type Object
2171          */
2172         OK :  [{
2173             name : 'ok',
2174             weight : 'primary',
2175             html : 'OK'
2176         }], 
2177         /**
2178          * Button config that displays Yes and No buttons
2179          * @type Object
2180          */
2181         YESNO : [
2182             {
2183                 name  : 'no',
2184                 html : 'No'
2185             },
2186             {
2187                 name  :'yes',
2188                 weight : 'primary',
2189                 html : 'Yes'
2190             }
2191         ],
2192         
2193         /**
2194          * Button config that displays OK and Cancel buttons
2195          * @type Object
2196          */
2197         OKCANCEL : [
2198             {
2199                name : 'cancel',
2200                 html : 'Cancel'
2201             },
2202             {
2203                 name : 'ok',
2204                 weight : 'primary',
2205                 html : 'OK'
2206             }
2207         ],
2208         /**
2209          * Button config that displays Yes, No and Cancel buttons
2210          * @type Object
2211          */
2212         YESNOCANCEL : [
2213             {
2214                 name : 'yes',
2215                 weight : 'primary',
2216                 html : 'Yes'
2217             },
2218             {
2219                 name : 'no',
2220                 html : 'No'
2221             },
2222             {
2223                 name : 'cancel',
2224                 html : 'Cancel'
2225             }
2226         ]
2227 });
2228  /*
2229  * - LGPL
2230  *
2231  * messagebox - can be used as a replace
2232  * 
2233  */
2234 /**
2235  * @class Roo.MessageBox
2236  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2237  * Example usage:
2238  *<pre><code>
2239 // Basic alert:
2240 Roo.Msg.alert('Status', 'Changes saved successfully.');
2241
2242 // Prompt for user data:
2243 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2244     if (btn == 'ok'){
2245         // process text value...
2246     }
2247 });
2248
2249 // Show a dialog using config options:
2250 Roo.Msg.show({
2251    title:'Save Changes?',
2252    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2253    buttons: Roo.Msg.YESNOCANCEL,
2254    fn: processResult,
2255    animEl: 'elId'
2256 });
2257 </code></pre>
2258  * @singleton
2259  */
2260 Roo.bootstrap.MessageBox = function(){
2261     var dlg, opt, mask, waitTimer;
2262     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2263     var buttons, activeTextEl, bwidth;
2264
2265     
2266     // private
2267     var handleButton = function(button){
2268         dlg.hide();
2269         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2270     };
2271
2272     // private
2273     var handleHide = function(){
2274         if(opt && opt.cls){
2275             dlg.el.removeClass(opt.cls);
2276         }
2277         //if(waitTimer){
2278         //    Roo.TaskMgr.stop(waitTimer);
2279         //    waitTimer = null;
2280         //}
2281     };
2282
2283     // private
2284     var updateButtons = function(b){
2285         var width = 0;
2286         if(!b){
2287             buttons["ok"].hide();
2288             buttons["cancel"].hide();
2289             buttons["yes"].hide();
2290             buttons["no"].hide();
2291             //dlg.footer.dom.style.display = 'none';
2292             return width;
2293         }
2294         dlg.footer.dom.style.display = '';
2295         for(var k in buttons){
2296             if(typeof buttons[k] != "function"){
2297                 if(b[k]){
2298                     buttons[k].show();
2299                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2300                     width += buttons[k].el.getWidth()+15;
2301                 }else{
2302                     buttons[k].hide();
2303                 }
2304             }
2305         }
2306         return width;
2307     };
2308
2309     // private
2310     var handleEsc = function(d, k, e){
2311         if(opt && opt.closable !== false){
2312             dlg.hide();
2313         }
2314         if(e){
2315             e.stopEvent();
2316         }
2317     };
2318
2319     return {
2320         /**
2321          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2322          * @return {Roo.BasicDialog} The BasicDialog element
2323          */
2324         getDialog : function(){
2325            if(!dlg){
2326                 dlg = new Roo.bootstrap.Modal( {
2327                     //draggable: true,
2328                     //resizable:false,
2329                     //constraintoviewport:false,
2330                     //fixedcenter:true,
2331                     //collapsible : false,
2332                     //shim:true,
2333                     //modal: true,
2334                   //  width:400,
2335                   //  height:100,
2336                     //buttonAlign:"center",
2337                     closeClick : function(){
2338                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2339                             handleButton("no");
2340                         }else{
2341                             handleButton("cancel");
2342                         }
2343                     }
2344                 });
2345                 dlg.render();
2346                 dlg.on("hide", handleHide);
2347                 mask = dlg.mask;
2348                 //dlg.addKeyListener(27, handleEsc);
2349                 buttons = {};
2350                 this.buttons = buttons;
2351                 var bt = this.buttonText;
2352                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2353                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2354                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2355                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2356                 Roo.log(buttons)
2357                 bodyEl = dlg.body.createChild({
2358
2359                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2360                         '<textarea class="roo-mb-textarea"></textarea>' +
2361                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2362                 });
2363                 msgEl = bodyEl.dom.firstChild;
2364                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2365                 textboxEl.enableDisplayMode();
2366                 textboxEl.addKeyListener([10,13], function(){
2367                     if(dlg.isVisible() && opt && opt.buttons){
2368                         if(opt.buttons.ok){
2369                             handleButton("ok");
2370                         }else if(opt.buttons.yes){
2371                             handleButton("yes");
2372                         }
2373                     }
2374                 });
2375                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2376                 textareaEl.enableDisplayMode();
2377                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2378                 progressEl.enableDisplayMode();
2379                 var pf = progressEl.dom.firstChild;
2380                 if (pf) {
2381                     pp = Roo.get(pf.firstChild);
2382                     pp.setHeight(pf.offsetHeight);
2383                 }
2384                 
2385             }
2386             return dlg;
2387         },
2388
2389         /**
2390          * Updates the message box body text
2391          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2392          * the XHTML-compliant non-breaking space character '&amp;#160;')
2393          * @return {Roo.MessageBox} This message box
2394          */
2395         updateText : function(text){
2396             if(!dlg.isVisible() && !opt.width){
2397                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2398             }
2399             msgEl.innerHTML = text || '&#160;';
2400       
2401             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2402             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2403             var w = Math.max(
2404                     Math.min(opt.width || cw , this.maxWidth), 
2405                     Math.max(opt.minWidth || this.minWidth, bwidth)
2406             );
2407             if(opt.prompt){
2408                 activeTextEl.setWidth(w);
2409             }
2410             if(dlg.isVisible()){
2411                 dlg.fixedcenter = false;
2412             }
2413             // to big, make it scroll. = But as usual stupid IE does not support
2414             // !important..
2415             
2416             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2417                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2418                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2419             } else {
2420                 bodyEl.dom.style.height = '';
2421                 bodyEl.dom.style.overflowY = '';
2422             }
2423             if (cw > w) {
2424                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2425             } else {
2426                 bodyEl.dom.style.overflowX = '';
2427             }
2428             
2429             dlg.setContentSize(w, bodyEl.getHeight());
2430             if(dlg.isVisible()){
2431                 dlg.fixedcenter = true;
2432             }
2433             return this;
2434         },
2435
2436         /**
2437          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2438          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2439          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2440          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2441          * @return {Roo.MessageBox} This message box
2442          */
2443         updateProgress : function(value, text){
2444             if(text){
2445                 this.updateText(text);
2446             }
2447             if (pp) { // weird bug on my firefox - for some reason this is not defined
2448                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2449             }
2450             return this;
2451         },        
2452
2453         /**
2454          * Returns true if the message box is currently displayed
2455          * @return {Boolean} True if the message box is visible, else false
2456          */
2457         isVisible : function(){
2458             return dlg && dlg.isVisible();  
2459         },
2460
2461         /**
2462          * Hides the message box if it is displayed
2463          */
2464         hide : function(){
2465             if(this.isVisible()){
2466                 dlg.hide();
2467             }  
2468         },
2469
2470         /**
2471          * Displays a new message box, or reinitializes an existing message box, based on the config options
2472          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2473          * The following config object properties are supported:
2474          * <pre>
2475 Property    Type             Description
2476 ----------  ---------------  ------------------------------------------------------------------------------------
2477 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2478                                    closes (defaults to undefined)
2479 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2480                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2481 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2482                                    progress and wait dialogs will ignore this property and always hide the
2483                                    close button as they can only be closed programmatically.
2484 cls               String           A custom CSS class to apply to the message box element
2485 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2486                                    displayed (defaults to 75)
2487 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2488                                    function will be btn (the name of the button that was clicked, if applicable,
2489                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2490                                    Progress and wait dialogs will ignore this option since they do not respond to
2491                                    user actions and can only be closed programmatically, so any required function
2492                                    should be called by the same code after it closes the dialog.
2493 icon              String           A CSS class that provides a background image to be used as an icon for
2494                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2495 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2496 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2497 modal             Boolean          False to allow user interaction with the page while the message box is
2498                                    displayed (defaults to true)
2499 msg               String           A string that will replace the existing message box body text (defaults
2500                                    to the XHTML-compliant non-breaking space character '&#160;')
2501 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2502 progress          Boolean          True to display a progress bar (defaults to false)
2503 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2504 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2505 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2506 title             String           The title text
2507 value             String           The string value to set into the active textbox element if displayed
2508 wait              Boolean          True to display a progress bar (defaults to false)
2509 width             Number           The width of the dialog in pixels
2510 </pre>
2511          *
2512          * Example usage:
2513          * <pre><code>
2514 Roo.Msg.show({
2515    title: 'Address',
2516    msg: 'Please enter your address:',
2517    width: 300,
2518    buttons: Roo.MessageBox.OKCANCEL,
2519    multiline: true,
2520    fn: saveAddress,
2521    animEl: 'addAddressBtn'
2522 });
2523 </code></pre>
2524          * @param {Object} config Configuration options
2525          * @return {Roo.MessageBox} This message box
2526          */
2527         show : function(options)
2528         {
2529             
2530             // this causes nightmares if you show one dialog after another
2531             // especially on callbacks..
2532              
2533             if(this.isVisible()){
2534                 
2535                 this.hide();
2536                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2537                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2538                 Roo.log("New Dialog Message:" +  options.msg )
2539                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2540                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2541                 
2542             }
2543             var d = this.getDialog();
2544             opt = options;
2545             d.setTitle(opt.title || "&#160;");
2546             d.close.setDisplayed(opt.closable !== false);
2547             activeTextEl = textboxEl;
2548             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2549             if(opt.prompt){
2550                 if(opt.multiline){
2551                     textboxEl.hide();
2552                     textareaEl.show();
2553                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2554                         opt.multiline : this.defaultTextHeight);
2555                     activeTextEl = textareaEl;
2556                 }else{
2557                     textboxEl.show();
2558                     textareaEl.hide();
2559                 }
2560             }else{
2561                 textboxEl.hide();
2562                 textareaEl.hide();
2563             }
2564             progressEl.setDisplayed(opt.progress === true);
2565             this.updateProgress(0);
2566             activeTextEl.dom.value = opt.value || "";
2567             if(opt.prompt){
2568                 dlg.setDefaultButton(activeTextEl);
2569             }else{
2570                 var bs = opt.buttons;
2571                 var db = null;
2572                 if(bs && bs.ok){
2573                     db = buttons["ok"];
2574                 }else if(bs && bs.yes){
2575                     db = buttons["yes"];
2576                 }
2577                 dlg.setDefaultButton(db);
2578             }
2579             bwidth = updateButtons(opt.buttons);
2580             this.updateText(opt.msg);
2581             if(opt.cls){
2582                 d.el.addClass(opt.cls);
2583             }
2584             d.proxyDrag = opt.proxyDrag === true;
2585             d.modal = opt.modal !== false;
2586             d.mask = opt.modal !== false ? mask : false;
2587             if(!d.isVisible()){
2588                 // force it to the end of the z-index stack so it gets a cursor in FF
2589                 document.body.appendChild(dlg.el.dom);
2590                 d.animateTarget = null;
2591                 d.show(options.animEl);
2592             }
2593             return this;
2594         },
2595
2596         /**
2597          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2598          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2599          * and closing the message box when the process is complete.
2600          * @param {String} title The title bar text
2601          * @param {String} msg The message box body text
2602          * @return {Roo.MessageBox} This message box
2603          */
2604         progress : function(title, msg){
2605             this.show({
2606                 title : title,
2607                 msg : msg,
2608                 buttons: false,
2609                 progress:true,
2610                 closable:false,
2611                 minWidth: this.minProgressWidth,
2612                 modal : true
2613             });
2614             return this;
2615         },
2616
2617         /**
2618          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2619          * If a callback function is passed it will be called after the user clicks the button, and the
2620          * id of the button that was clicked will be passed as the only parameter to the callback
2621          * (could also be the top-right close button).
2622          * @param {String} title The title bar text
2623          * @param {String} msg The message box body text
2624          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2625          * @param {Object} scope (optional) The scope of the callback function
2626          * @return {Roo.MessageBox} This message box
2627          */
2628         alert : function(title, msg, fn, scope){
2629             this.show({
2630                 title : title,
2631                 msg : msg,
2632                 buttons: this.OK,
2633                 fn: fn,
2634                 scope : scope,
2635                 modal : true
2636             });
2637             return this;
2638         },
2639
2640         /**
2641          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2642          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2643          * You are responsible for closing the message box when the process is complete.
2644          * @param {String} msg The message box body text
2645          * @param {String} title (optional) The title bar text
2646          * @return {Roo.MessageBox} This message box
2647          */
2648         wait : function(msg, title){
2649             this.show({
2650                 title : title,
2651                 msg : msg,
2652                 buttons: false,
2653                 closable:false,
2654                 progress:true,
2655                 modal:true,
2656                 width:300,
2657                 wait:true
2658             });
2659             waitTimer = Roo.TaskMgr.start({
2660                 run: function(i){
2661                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2662                 },
2663                 interval: 1000
2664             });
2665             return this;
2666         },
2667
2668         /**
2669          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2670          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2671          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2672          * @param {String} title The title bar text
2673          * @param {String} msg The message box body text
2674          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2675          * @param {Object} scope (optional) The scope of the callback function
2676          * @return {Roo.MessageBox} This message box
2677          */
2678         confirm : function(title, msg, fn, scope){
2679             this.show({
2680                 title : title,
2681                 msg : msg,
2682                 buttons: this.YESNO,
2683                 fn: fn,
2684                 scope : scope,
2685                 modal : true
2686             });
2687             return this;
2688         },
2689
2690         /**
2691          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2692          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2693          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2694          * (could also be the top-right close button) and the text that was entered will be passed as the two
2695          * parameters to the callback.
2696          * @param {String} title The title bar text
2697          * @param {String} msg The message box body text
2698          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2699          * @param {Object} scope (optional) The scope of the callback function
2700          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2701          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2702          * @return {Roo.MessageBox} This message box
2703          */
2704         prompt : function(title, msg, fn, scope, multiline){
2705             this.show({
2706                 title : title,
2707                 msg : msg,
2708                 buttons: this.OKCANCEL,
2709                 fn: fn,
2710                 minWidth:250,
2711                 scope : scope,
2712                 prompt:true,
2713                 multiline: multiline,
2714                 modal : true
2715             });
2716             return this;
2717         },
2718
2719         /**
2720          * Button config that displays a single OK button
2721          * @type Object
2722          */
2723         OK : {ok:true},
2724         /**
2725          * Button config that displays Yes and No buttons
2726          * @type Object
2727          */
2728         YESNO : {yes:true, no:true},
2729         /**
2730          * Button config that displays OK and Cancel buttons
2731          * @type Object
2732          */
2733         OKCANCEL : {ok:true, cancel:true},
2734         /**
2735          * Button config that displays Yes, No and Cancel buttons
2736          * @type Object
2737          */
2738         YESNOCANCEL : {yes:true, no:true, cancel:true},
2739
2740         /**
2741          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2742          * @type Number
2743          */
2744         defaultTextHeight : 75,
2745         /**
2746          * The maximum width in pixels of the message box (defaults to 600)
2747          * @type Number
2748          */
2749         maxWidth : 600,
2750         /**
2751          * The minimum width in pixels of the message box (defaults to 100)
2752          * @type Number
2753          */
2754         minWidth : 100,
2755         /**
2756          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2757          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2758          * @type Number
2759          */
2760         minProgressWidth : 250,
2761         /**
2762          * An object containing the default button text strings that can be overriden for localized language support.
2763          * Supported properties are: ok, cancel, yes and no.
2764          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2765          * @type Object
2766          */
2767         buttonText : {
2768             ok : "OK",
2769             cancel : "Cancel",
2770             yes : "Yes",
2771             no : "No"
2772         }
2773     };
2774 }();
2775
2776 /**
2777  * Shorthand for {@link Roo.MessageBox}
2778  */
2779 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2780 Roo.Msg = Roo.Msg || Roo.MessageBox;
2781 /*
2782  * - LGPL
2783  *
2784  * navbar
2785  * 
2786  */
2787
2788 /**
2789  * @class Roo.bootstrap.Navbar
2790  * @extends Roo.bootstrap.Component
2791  * Bootstrap Navbar class
2792
2793  * @constructor
2794  * Create a new Navbar
2795  * @param {Object} config The config object
2796  */
2797
2798
2799 Roo.bootstrap.Navbar = function(config){
2800     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2801     
2802 };
2803
2804 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2805     
2806     
2807    
2808     // private
2809     navItems : false,
2810     loadMask : false,
2811     
2812     
2813     getAutoCreate : function(){
2814         
2815         
2816         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2817         
2818     },
2819     
2820     initEvents :function ()
2821     {
2822         //Roo.log(this.el.select('.navbar-toggle',true));
2823         this.el.select('.navbar-toggle',true).on('click', function() {
2824            // Roo.log('click');
2825             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2826         }, this);
2827         
2828         var mark = {
2829             tag: "div",
2830             cls:"x-dlg-mask"
2831         }
2832         
2833         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2834         
2835         var size = this.el.getSize();
2836         this.maskEl.setSize(size.width, size.height);
2837         this.maskEl.enableDisplayMode("block");
2838         this.maskEl.hide();
2839         
2840         if(this.loadMask){
2841             this.maskEl.show();
2842         }
2843     },
2844     
2845     
2846     getChildContainer : function()
2847     {
2848         if (this.el.select('.collapse').getCount()) {
2849             return this.el.select('.collapse',true).first();
2850         }
2851         
2852         return this.el;
2853     },
2854     
2855     mask : function()
2856     {
2857         this.maskEl.show();
2858     },
2859     
2860     unmask : function()
2861     {
2862         this.maskEl.hide();
2863     } 
2864     
2865     
2866     
2867     
2868 });
2869
2870
2871
2872  
2873
2874  /*
2875  * - LGPL
2876  *
2877  * navbar
2878  * 
2879  */
2880
2881 /**
2882  * @class Roo.bootstrap.NavSimplebar
2883  * @extends Roo.bootstrap.Navbar
2884  * Bootstrap Sidebar class
2885  *
2886  * @cfg {Boolean} inverse is inverted color
2887  * 
2888  * @cfg {String} type (nav | pills | tabs)
2889  * @cfg {Boolean} arrangement stacked | justified
2890  * @cfg {String} align (left | right) alignment
2891  * 
2892  * @cfg {Boolean} main (true|false) main nav bar? default false
2893  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2894  * 
2895  * @cfg {String} tag (header|footer|nav|div) default is nav 
2896
2897  * 
2898  * 
2899  * 
2900  * @constructor
2901  * Create a new Sidebar
2902  * @param {Object} config The config object
2903  */
2904
2905
2906 Roo.bootstrap.NavSimplebar = function(config){
2907     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2908 };
2909
2910 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2911     
2912     inverse: false,
2913     
2914     type: false,
2915     arrangement: '',
2916     align : false,
2917     
2918     
2919     
2920     main : false,
2921     
2922     
2923     tag : false,
2924     
2925     
2926     getAutoCreate : function(){
2927         
2928         
2929         var cfg = {
2930             tag : this.tag || 'div',
2931             cls : 'navbar'
2932         };
2933           
2934         
2935         cfg.cn = [
2936             {
2937                 cls: 'nav',
2938                 tag : 'ul'
2939             }
2940         ];
2941         
2942          
2943         this.type = this.type || 'nav';
2944         if (['tabs','pills'].indexOf(this.type)!==-1) {
2945             cfg.cn[0].cls += ' nav-' + this.type
2946         
2947         
2948         } else {
2949             if (this.type!=='nav') {
2950                 Roo.log('nav type must be nav/tabs/pills')
2951             }
2952             cfg.cn[0].cls += ' navbar-nav'
2953         }
2954         
2955         
2956         
2957         
2958         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2959             cfg.cn[0].cls += ' nav-' + this.arrangement;
2960         }
2961         
2962         
2963         if (this.align === 'right') {
2964             cfg.cn[0].cls += ' navbar-right';
2965         }
2966         
2967         if (this.inverse) {
2968             cfg.cls += ' navbar-inverse';
2969             
2970         }
2971         
2972         
2973         return cfg;
2974     
2975         
2976     }
2977     
2978     
2979     
2980 });
2981
2982
2983
2984  
2985
2986  
2987        /*
2988  * - LGPL
2989  *
2990  * navbar
2991  * 
2992  */
2993
2994 /**
2995  * @class Roo.bootstrap.NavHeaderbar
2996  * @extends Roo.bootstrap.NavSimplebar
2997  * Bootstrap Sidebar class
2998  *
2999  * @cfg {String} brand what is brand
3000  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3001  * @cfg {String} brand_href href of the brand
3002  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3003  * 
3004  * @constructor
3005  * Create a new Sidebar
3006  * @param {Object} config The config object
3007  */
3008
3009
3010 Roo.bootstrap.NavHeaderbar = function(config){
3011     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3012 };
3013
3014 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3015     
3016     position: '',
3017     brand: '',
3018     brand_href: false,
3019     srButton : true,
3020     
3021     
3022     getAutoCreate : function(){
3023         
3024         var   cfg = {
3025             tag: this.nav || 'nav',
3026             cls: 'navbar',
3027             role: 'navigation',
3028             cn: []
3029         };
3030         
3031         if(this.srButton){
3032             cfg.cn.push({
3033                 tag: 'div',
3034                 cls: 'navbar-header',
3035                 cn: [
3036                     {
3037                         tag: 'button',
3038                         type: 'button',
3039                         cls: 'navbar-toggle',
3040                         'data-toggle': 'collapse',
3041                         cn: [
3042                             {
3043                                 tag: 'span',
3044                                 cls: 'sr-only',
3045                                 html: 'Toggle navigation'
3046                             },
3047                             {
3048                                 tag: 'span',
3049                                 cls: 'icon-bar'
3050                             },
3051                             {
3052                                 tag: 'span',
3053                                 cls: 'icon-bar'
3054                             },
3055                             {
3056                                 tag: 'span',
3057                                 cls: 'icon-bar'
3058                             }
3059                         ]
3060                     }
3061                 ]
3062             });
3063         }
3064         
3065         cfg.cn.push({
3066             tag: 'div',
3067             cls: 'collapse navbar-collapse',
3068             cn : []
3069         });
3070         
3071         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3072         
3073         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3074             cfg.cls += ' navbar-' + this.position;
3075             
3076             // tag can override this..
3077             
3078             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3079         }
3080         
3081         if (this.brand !== '') {
3082             cfg.cn[0].cn.push({
3083                 tag: 'a',
3084                 href: this.brand_href ? this.brand_href : '#',
3085                 cls: 'navbar-brand',
3086                 cn: [
3087                 this.brand
3088                 ]
3089             });
3090         }
3091         
3092         if(this.main){
3093             cfg.cls += ' main-nav';
3094         }
3095         
3096         
3097         return cfg;
3098
3099         
3100     }
3101     
3102     
3103     
3104 });
3105
3106
3107
3108  
3109
3110  /*
3111  * - LGPL
3112  *
3113  * navbar
3114  * 
3115  */
3116
3117 /**
3118  * @class Roo.bootstrap.NavSidebar
3119  * @extends Roo.bootstrap.Navbar
3120  * Bootstrap Sidebar class
3121  * 
3122  * @constructor
3123  * Create a new Sidebar
3124  * @param {Object} config The config object
3125  */
3126
3127
3128 Roo.bootstrap.NavSidebar = function(config){
3129     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3130 };
3131
3132 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3133     
3134     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3135     
3136     getAutoCreate : function(){
3137         
3138         
3139         return  {
3140             tag: 'div',
3141             cls: 'sidebar sidebar-nav'
3142         };
3143     
3144         
3145     }
3146     
3147     
3148     
3149 });
3150
3151
3152
3153  
3154
3155  /*
3156  * - LGPL
3157  *
3158  * nav group
3159  * 
3160  */
3161
3162 /**
3163  * @class Roo.bootstrap.NavGroup
3164  * @extends Roo.bootstrap.Component
3165  * Bootstrap NavGroup class
3166  * @cfg {String} align left | right
3167  * @cfg {Boolean} inverse false | true
3168  * @cfg {String} type (nav|pills|tab) default nav
3169  * @cfg {String} navId - reference Id for navbar.
3170
3171  * 
3172  * @constructor
3173  * Create a new nav group
3174  * @param {Object} config The config object
3175  */
3176
3177 Roo.bootstrap.NavGroup = function(config){
3178     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3179     this.navItems = [];
3180     Roo.bootstrap.NavGroup.register(this);
3181      this.addEvents({
3182         /**
3183              * @event changed
3184              * Fires when the active item changes
3185              * @param {Roo.bootstrap.NavGroup} this
3186              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3187              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3188          */
3189         'changed': true
3190      });
3191     
3192 };
3193
3194 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3195     
3196     align: '',
3197     inverse: false,
3198     form: false,
3199     type: 'nav',
3200     navId : '',
3201     // private
3202     
3203     navItems : false,
3204     
3205     getAutoCreate : function()
3206     {
3207         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3208         
3209         cfg = {
3210             tag : 'ul',
3211             cls: 'nav' 
3212         }
3213         
3214         if (['tabs','pills'].indexOf(this.type)!==-1) {
3215             cfg.cls += ' nav-' + this.type
3216         } else {
3217             if (this.type!=='nav') {
3218                 Roo.log('nav type must be nav/tabs/pills')
3219             }
3220             cfg.cls += ' navbar-nav'
3221         }
3222         
3223         if (this.parent().sidebar) {
3224             cfg = {
3225                 tag: 'ul',
3226                 cls: 'dashboard-menu sidebar-menu'
3227             }
3228             
3229             return cfg;
3230         }
3231         
3232         if (this.form === true) {
3233             cfg = {
3234                 tag: 'form',
3235                 cls: 'navbar-form'
3236             }
3237             
3238             if (this.align === 'right') {
3239                 cfg.cls += ' navbar-right';
3240             } else {
3241                 cfg.cls += ' navbar-left';
3242             }
3243         }
3244         
3245         if (this.align === 'right') {
3246             cfg.cls += ' navbar-right';
3247         }
3248         
3249         if (this.inverse) {
3250             cfg.cls += ' navbar-inverse';
3251             
3252         }
3253         
3254         
3255         return cfg;
3256     },
3257     
3258     setActiveItem : function(item)
3259     {
3260         var prev = false;
3261         Roo.each(this.navItems, function(v){
3262             if (v == item) {
3263                 return ;
3264             }
3265             if (v.isActive()) {
3266                 v.setActive(false, true);
3267                 prev = v;
3268                 
3269             }
3270             
3271         });
3272
3273         item.setActive(true, true);
3274         this.fireEvent('changed', this, item, prev);
3275         
3276         
3277     },
3278     
3279     addItem : function(cfg)
3280     {
3281         var cn = new Roo.bootstrap.NavItem(cfg);
3282         this.register(cn);
3283         cn.parentId = this.id;
3284         cn.onRender(this.el, null);
3285         return cn;
3286     },
3287     
3288     register : function(item)
3289     {
3290         this.navItems.push( item);
3291         item.navId = this.navId;
3292     
3293     },
3294     getNavItem: function(tabId)
3295     {
3296         var ret = false;
3297         Roo.each(this.navItems, function(e) {
3298             if (e.tabId == tabId) {
3299                ret =  e;
3300                return false;
3301             }
3302             return true;
3303             
3304         });
3305         return ret;
3306     }
3307     
3308     
3309     
3310     
3311 });
3312
3313  
3314 Roo.apply(Roo.bootstrap.NavGroup, {
3315     
3316     groups: {},
3317     
3318     register : function(navgrp)
3319     {
3320         this.groups[navgrp.navId] = navgrp;
3321         
3322     },
3323     get: function(navId) {
3324         return this.groups[navId];
3325     }
3326     
3327     
3328     
3329 });
3330
3331  /*
3332  * - LGPL
3333  *
3334  * row
3335  * 
3336  */
3337
3338 /**
3339  * @class Roo.bootstrap.NavItem
3340  * @extends Roo.bootstrap.Component
3341  * Bootstrap Navbar.NavItem class
3342  * @cfg {String} href  link to
3343  * @cfg {String} html content of button
3344  * @cfg {String} badge text inside badge
3345  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3346  * @cfg {String} glyphicon name of glyphicon
3347  * @cfg {String} icon name of font awesome icon
3348  * @cfg {Boolean} active Is item active
3349  * @cfg {Boolean} disabled Is item disabled
3350  
3351  * @cfg {Boolean} preventDefault (true | false) default false
3352  * @cfg {String} tabId the tab that this item activates.
3353  * @cfg {String} tagtype (a|span) render as a href or span?
3354   
3355  * @constructor
3356  * Create a new Navbar Item
3357  * @param {Object} config The config object
3358  */
3359 Roo.bootstrap.NavItem = function(config){
3360     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3361     this.addEvents({
3362         // raw events
3363         /**
3364          * @event click
3365          * The raw click event for the entire grid.
3366          * @param {Roo.EventObject} e
3367          */
3368         "click" : true,
3369          /**
3370             * @event changed
3371             * Fires when the active item active state changes
3372             * @param {Roo.bootstrap.NavItem} this
3373             * @param {boolean} state the new state
3374              
3375          */
3376         'changed': true
3377     });
3378    
3379 };
3380
3381 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3382     
3383     href: false,
3384     html: '',
3385     badge: '',
3386     icon: false,
3387     glyphicon: false,
3388     active: false,
3389     preventDefault : false,
3390     tabId : false,
3391     tagtype : 'a',
3392     disabled : false,
3393     
3394     getAutoCreate : function(){
3395          
3396         var cfg = {
3397             tag: 'li',
3398             cls: 'nav-item'
3399             
3400         }
3401         if (this.active) {
3402             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3403         }
3404         if (this.disabled) {
3405             cfg.cls += ' disabled';
3406         }
3407         
3408         if (this.href || this.html || this.glyphicon || this.icon) {
3409             cfg.cn = [
3410                 {
3411                     tag: this.tagtype,
3412                     href : this.href || "#",
3413                     html: this.html || ''
3414                 }
3415             ];
3416             
3417             if (this.icon) {
3418                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3419             }
3420
3421             if(this.glyphicon) {
3422                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3423             }
3424             
3425             if (this.menu) {
3426                 
3427                 cfg.cn[0].html += " <span class='caret'></span>";
3428              
3429             }
3430             
3431             if (this.badge !== '') {
3432                  
3433                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3434             }
3435         }
3436         
3437         
3438         
3439         return cfg;
3440     },
3441     initEvents: function() {
3442        // Roo.log('init events?');
3443        // Roo.log(this.el.dom);
3444         if (typeof (this.menu) != 'undefined') {
3445             this.menu.parentType = this.xtype;
3446             this.menu.triggerEl = this.el;
3447             this.addxtype(Roo.apply({}, this.menu));
3448         }
3449
3450        
3451         this.el.select('a',true).on('click', this.onClick, this);
3452         // at this point parent should be available..
3453         this.parent().register(this);
3454     },
3455     
3456     onClick : function(e)
3457     {
3458          
3459         if(this.preventDefault){
3460             e.preventDefault();
3461         }
3462         if (this.disabled) {
3463             return;
3464         }
3465         Roo.log("fire event clicked");
3466         if(this.fireEvent('click', this, e) === false){
3467             return;
3468         };
3469         
3470         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3471             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3472                 this.parent().setActiveItem(this);
3473             }
3474             
3475             
3476             
3477         } 
3478     },
3479     
3480     isActive: function () {
3481         return this.active
3482     },
3483     setActive : function(state, fire)
3484     {
3485         this.active = state;
3486         if (!state ) {
3487             this.el.removeClass('active');
3488         } else if (!this.el.hasClass('active')) {
3489             this.el.addClass('active');
3490         }
3491         if (fire) {
3492             this.fireEvent('changed', this, state);
3493         }
3494         
3495         
3496     },
3497      // this should not be here...
3498     setDisabled : function(state)
3499     {
3500         this.disabled = state;
3501         if (!state ) {
3502             this.el.removeClass('disabled');
3503         } else if (!this.el.hasClass('disabled')) {
3504             this.el.addClass('disabled');
3505         }
3506         
3507     }
3508 });
3509  
3510
3511  /*
3512  * - LGPL
3513  *
3514  * sidebar item
3515  *
3516  *  li
3517  *    <span> icon </span>
3518  *    <span> text </span>
3519  *    <span>badge </span>
3520  */
3521
3522 /**
3523  * @class Roo.bootstrap.NavSidebarItem
3524  * @extends Roo.bootstrap.NavItem
3525  * Bootstrap Navbar.NavSidebarItem class
3526  * @constructor
3527  * Create a new Navbar Button
3528  * @param {Object} config The config object
3529  */
3530 Roo.bootstrap.NavSidebarItem = function(config){
3531     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3532     this.addEvents({
3533         // raw events
3534         /**
3535          * @event click
3536          * The raw click event for the entire grid.
3537          * @param {Roo.EventObject} e
3538          */
3539         "click" : true,
3540          /**
3541             * @event changed
3542             * Fires when the active item active state changes
3543             * @param {Roo.bootstrap.NavSidebarItem} this
3544             * @param {boolean} state the new state
3545              
3546          */
3547         'changed': true
3548     });
3549    
3550 };
3551
3552 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3553     
3554     
3555     getAutoCreate : function(){
3556         
3557         
3558         var a = {
3559                 tag: 'a',
3560                 href : this.href || '#',
3561                 cls: '',
3562                 html : '',
3563                 cn : []
3564         };
3565         var cfg = {
3566             tag: 'li',
3567             cls: '',
3568             cn: [ a ]
3569         }
3570         var span = {
3571             tag: 'span',
3572             html : this.html || ''
3573         }
3574         
3575         
3576         if (this.active) {
3577             cfg.cls += ' active';
3578         }
3579         
3580         // left icon..
3581         if (this.glyphicon || this.icon) {
3582             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3583             a.cn.push({ tag : 'i', cls : c }) ;
3584         }
3585         // html..
3586         a.cn.push(span);
3587         // then badge..
3588         if (this.badge !== '') {
3589             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3590         }
3591         // fi
3592         if (this.menu) {
3593             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3594             a.cls += 'dropdown-toggle treeview' ;
3595             
3596         }
3597         
3598         
3599         
3600         return cfg;
3601          
3602            
3603     }
3604    
3605      
3606  
3607 });
3608  
3609
3610  /*
3611  * - LGPL
3612  *
3613  * row
3614  * 
3615  */
3616
3617 /**
3618  * @class Roo.bootstrap.Row
3619  * @extends Roo.bootstrap.Component
3620  * Bootstrap Row class (contains columns...)
3621  * 
3622  * @constructor
3623  * Create a new Row
3624  * @param {Object} config The config object
3625  */
3626
3627 Roo.bootstrap.Row = function(config){
3628     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3629 };
3630
3631 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3632     
3633     getAutoCreate : function(){
3634        return {
3635             cls: 'row clearfix'
3636        };
3637     }
3638     
3639     
3640 });
3641
3642  
3643
3644  /*
3645  * - LGPL
3646  *
3647  * element
3648  * 
3649  */
3650
3651 /**
3652  * @class Roo.bootstrap.Element
3653  * @extends Roo.bootstrap.Component
3654  * Bootstrap Element class
3655  * @cfg {String} html contents of the element
3656  * @cfg {String} tag tag of the element
3657  * @cfg {String} cls class of the element
3658  * 
3659  * @constructor
3660  * Create a new Element
3661  * @param {Object} config The config object
3662  */
3663
3664 Roo.bootstrap.Element = function(config){
3665     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3666 };
3667
3668 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3669     
3670     tag: 'div',
3671     cls: '',
3672     html: '',
3673      
3674     
3675     getAutoCreate : function(){
3676         
3677         var cfg = {
3678             tag: this.tag,
3679             cls: this.cls,
3680             html: this.html
3681         }
3682         
3683         
3684         
3685         return cfg;
3686     }
3687    
3688 });
3689
3690  
3691
3692  /*
3693  * - LGPL
3694  *
3695  * pagination
3696  * 
3697  */
3698
3699 /**
3700  * @class Roo.bootstrap.Pagination
3701  * @extends Roo.bootstrap.Component
3702  * Bootstrap Pagination class
3703  * @cfg {String} size xs | sm | md | lg
3704  * @cfg {Boolean} inverse false | true
3705  * 
3706  * @constructor
3707  * Create a new Pagination
3708  * @param {Object} config The config object
3709  */
3710
3711 Roo.bootstrap.Pagination = function(config){
3712     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3713 };
3714
3715 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3716     
3717     cls: false,
3718     size: false,
3719     inverse: false,
3720     
3721     getAutoCreate : function(){
3722         var cfg = {
3723             tag: 'ul',
3724                 cls: 'pagination'
3725         };
3726         if (this.inverse) {
3727             cfg.cls += ' inverse';
3728         }
3729         if (this.html) {
3730             cfg.html=this.html;
3731         }
3732         if (this.cls) {
3733             cfg.cls += " " + this.cls;
3734         }
3735         return cfg;
3736     }
3737    
3738 });
3739
3740  
3741
3742  /*
3743  * - LGPL
3744  *
3745  * Pagination item
3746  * 
3747  */
3748
3749
3750 /**
3751  * @class Roo.bootstrap.PaginationItem
3752  * @extends Roo.bootstrap.Component
3753  * Bootstrap PaginationItem class
3754  * @cfg {String} html text
3755  * @cfg {String} href the link
3756  * @cfg {Boolean} preventDefault (true | false) default true
3757  * @cfg {Boolean} active (true | false) default false
3758  * 
3759  * 
3760  * @constructor
3761  * Create a new PaginationItem
3762  * @param {Object} config The config object
3763  */
3764
3765
3766 Roo.bootstrap.PaginationItem = function(config){
3767     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3768     this.addEvents({
3769         // raw events
3770         /**
3771          * @event click
3772          * The raw click event for the entire grid.
3773          * @param {Roo.EventObject} e
3774          */
3775         "click" : true
3776     });
3777 };
3778
3779 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3780     
3781     href : false,
3782     html : false,
3783     preventDefault: true,
3784     active : false,
3785     cls : false,
3786     
3787     getAutoCreate : function(){
3788         var cfg= {
3789             tag: 'li',
3790             cn: [
3791                 {
3792                     tag : 'a',
3793                     href : this.href ? this.href : '#',
3794                     html : this.html ? this.html : ''
3795                 }
3796             ]
3797         };
3798         
3799         if(this.cls){
3800             cfg.cls = this.cls;
3801         }
3802         
3803         if(this.active){
3804             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3805         }
3806         
3807         return cfg;
3808     },
3809     
3810     initEvents: function() {
3811         
3812         this.el.on('click', this.onClick, this);
3813         
3814     },
3815     onClick : function(e)
3816     {
3817         Roo.log('PaginationItem on click ');
3818         if(this.preventDefault){
3819             e.preventDefault();
3820         }
3821         
3822         this.fireEvent('click', this, e);
3823     }
3824    
3825 });
3826
3827  
3828
3829  /*
3830  * - LGPL
3831  *
3832  * slider
3833  * 
3834  */
3835
3836
3837 /**
3838  * @class Roo.bootstrap.Slider
3839  * @extends Roo.bootstrap.Component
3840  * Bootstrap Slider class
3841  *    
3842  * @constructor
3843  * Create a new Slider
3844  * @param {Object} config The config object
3845  */
3846
3847 Roo.bootstrap.Slider = function(config){
3848     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3849 };
3850
3851 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3852     
3853     getAutoCreate : function(){
3854         
3855         var cfg = {
3856             tag: 'div',
3857             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3858             cn: [
3859                 {
3860                     tag: 'a',
3861                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3862                 }
3863             ]
3864         }
3865         
3866         return cfg;
3867     }
3868    
3869 });
3870
3871  /*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881  
3882
3883 /**
3884  * @class Roo.grid.ColumnModel
3885  * @extends Roo.util.Observable
3886  * This is the default implementation of a ColumnModel used by the Grid. It defines
3887  * the columns in the grid.
3888  * <br>Usage:<br>
3889  <pre><code>
3890  var colModel = new Roo.grid.ColumnModel([
3891         {header: "Ticker", width: 60, sortable: true, locked: true},
3892         {header: "Company Name", width: 150, sortable: true},
3893         {header: "Market Cap.", width: 100, sortable: true},
3894         {header: "$ Sales", width: 100, sortable: true, renderer: money},
3895         {header: "Employees", width: 100, sortable: true, resizable: false}
3896  ]);
3897  </code></pre>
3898  * <p>
3899  
3900  * The config options listed for this class are options which may appear in each
3901  * individual column definition.
3902  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
3903  * @constructor
3904  * @param {Object} config An Array of column config objects. See this class's
3905  * config objects for details.
3906 */
3907 Roo.grid.ColumnModel = function(config){
3908         /**
3909      * The config passed into the constructor
3910      */
3911     this.config = config;
3912     this.lookup = {};
3913
3914     // if no id, create one
3915     // if the column does not have a dataIndex mapping,
3916     // map it to the order it is in the config
3917     for(var i = 0, len = config.length; i < len; i++){
3918         var c = config[i];
3919         if(typeof c.dataIndex == "undefined"){
3920             c.dataIndex = i;
3921         }
3922         if(typeof c.renderer == "string"){
3923             c.renderer = Roo.util.Format[c.renderer];
3924         }
3925         if(typeof c.id == "undefined"){
3926             c.id = Roo.id();
3927         }
3928         if(c.editor && c.editor.xtype){
3929             c.editor  = Roo.factory(c.editor, Roo.grid);
3930         }
3931         if(c.editor && c.editor.isFormField){
3932             c.editor = new Roo.grid.GridEditor(c.editor);
3933         }
3934         this.lookup[c.id] = c;
3935     }
3936
3937     /**
3938      * The width of columns which have no width specified (defaults to 100)
3939      * @type Number
3940      */
3941     this.defaultWidth = 100;
3942
3943     /**
3944      * Default sortable of columns which have no sortable specified (defaults to false)
3945      * @type Boolean
3946      */
3947     this.defaultSortable = false;
3948
3949     this.addEvents({
3950         /**
3951              * @event widthchange
3952              * Fires when the width of a column changes.
3953              * @param {ColumnModel} this
3954              * @param {Number} columnIndex The column index
3955              * @param {Number} newWidth The new width
3956              */
3957             "widthchange": true,
3958         /**
3959              * @event headerchange
3960              * Fires when the text of a header changes.
3961              * @param {ColumnModel} this
3962              * @param {Number} columnIndex The column index
3963              * @param {Number} newText The new header text
3964              */
3965             "headerchange": true,
3966         /**
3967              * @event hiddenchange
3968              * Fires when a column is hidden or "unhidden".
3969              * @param {ColumnModel} this
3970              * @param {Number} columnIndex The column index
3971              * @param {Boolean} hidden true if hidden, false otherwise
3972              */
3973             "hiddenchange": true,
3974             /**
3975          * @event columnmoved
3976          * Fires when a column is moved.
3977          * @param {ColumnModel} this
3978          * @param {Number} oldIndex
3979          * @param {Number} newIndex
3980          */
3981         "columnmoved" : true,
3982         /**
3983          * @event columlockchange
3984          * Fires when a column's locked state is changed
3985          * @param {ColumnModel} this
3986          * @param {Number} colIndex
3987          * @param {Boolean} locked true if locked
3988          */
3989         "columnlockchange" : true
3990     });
3991     Roo.grid.ColumnModel.superclass.constructor.call(this);
3992 };
3993 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
3994     /**
3995      * @cfg {String} header The header text to display in the Grid view.
3996      */
3997     /**
3998      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
3999      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4000      * specified, the column's index is used as an index into the Record's data Array.
4001      */
4002     /**
4003      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4004      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4005      */
4006     /**
4007      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4008      * Defaults to the value of the {@link #defaultSortable} property.
4009      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4010      */
4011     /**
4012      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4013      */
4014     /**
4015      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4016      */
4017     /**
4018      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4019      */
4020     /**
4021      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4022      */
4023     /**
4024      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4025      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4026      * default renderer uses the raw data value.
4027      */
4028        /**
4029      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4030      */
4031     /**
4032      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4033      */
4034
4035     /**
4036      * Returns the id of the column at the specified index.
4037      * @param {Number} index The column index
4038      * @return {String} the id
4039      */
4040     getColumnId : function(index){
4041         return this.config[index].id;
4042     },
4043
4044     /**
4045      * Returns the column for a specified id.
4046      * @param {String} id The column id
4047      * @return {Object} the column
4048      */
4049     getColumnById : function(id){
4050         return this.lookup[id];
4051     },
4052
4053     
4054     /**
4055      * Returns the column for a specified dataIndex.
4056      * @param {String} dataIndex The column dataIndex
4057      * @return {Object|Boolean} the column or false if not found
4058      */
4059     getColumnByDataIndex: function(dataIndex){
4060         var index = this.findColumnIndex(dataIndex);
4061         return index > -1 ? this.config[index] : false;
4062     },
4063     
4064     /**
4065      * Returns the index for a specified column id.
4066      * @param {String} id The column id
4067      * @return {Number} the index, or -1 if not found
4068      */
4069     getIndexById : function(id){
4070         for(var i = 0, len = this.config.length; i < len; i++){
4071             if(this.config[i].id == id){
4072                 return i;
4073             }
4074         }
4075         return -1;
4076     },
4077     
4078     /**
4079      * Returns the index for a specified column dataIndex.
4080      * @param {String} dataIndex The column dataIndex
4081      * @return {Number} the index, or -1 if not found
4082      */
4083     
4084     findColumnIndex : function(dataIndex){
4085         for(var i = 0, len = this.config.length; i < len; i++){
4086             if(this.config[i].dataIndex == dataIndex){
4087                 return i;
4088             }
4089         }
4090         return -1;
4091     },
4092     
4093     
4094     moveColumn : function(oldIndex, newIndex){
4095         var c = this.config[oldIndex];
4096         this.config.splice(oldIndex, 1);
4097         this.config.splice(newIndex, 0, c);
4098         this.dataMap = null;
4099         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4100     },
4101
4102     isLocked : function(colIndex){
4103         return this.config[colIndex].locked === true;
4104     },
4105
4106     setLocked : function(colIndex, value, suppressEvent){
4107         if(this.isLocked(colIndex) == value){
4108             return;
4109         }
4110         this.config[colIndex].locked = value;
4111         if(!suppressEvent){
4112             this.fireEvent("columnlockchange", this, colIndex, value);
4113         }
4114     },
4115
4116     getTotalLockedWidth : function(){
4117         var totalWidth = 0;
4118         for(var i = 0; i < this.config.length; i++){
4119             if(this.isLocked(i) && !this.isHidden(i)){
4120                 this.totalWidth += this.getColumnWidth(i);
4121             }
4122         }
4123         return totalWidth;
4124     },
4125
4126     getLockedCount : function(){
4127         for(var i = 0, len = this.config.length; i < len; i++){
4128             if(!this.isLocked(i)){
4129                 return i;
4130             }
4131         }
4132     },
4133
4134     /**
4135      * Returns the number of columns.
4136      * @return {Number}
4137      */
4138     getColumnCount : function(visibleOnly){
4139         if(visibleOnly === true){
4140             var c = 0;
4141             for(var i = 0, len = this.config.length; i < len; i++){
4142                 if(!this.isHidden(i)){
4143                     c++;
4144                 }
4145             }
4146             return c;
4147         }
4148         return this.config.length;
4149     },
4150
4151     /**
4152      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4153      * @param {Function} fn
4154      * @param {Object} scope (optional)
4155      * @return {Array} result
4156      */
4157     getColumnsBy : function(fn, scope){
4158         var r = [];
4159         for(var i = 0, len = this.config.length; i < len; i++){
4160             var c = this.config[i];
4161             if(fn.call(scope||this, c, i) === true){
4162                 r[r.length] = c;
4163             }
4164         }
4165         return r;
4166     },
4167
4168     /**
4169      * Returns true if the specified column is sortable.
4170      * @param {Number} col The column index
4171      * @return {Boolean}
4172      */
4173     isSortable : function(col){
4174         if(typeof this.config[col].sortable == "undefined"){
4175             return this.defaultSortable;
4176         }
4177         return this.config[col].sortable;
4178     },
4179
4180     /**
4181      * Returns the rendering (formatting) function defined for the column.
4182      * @param {Number} col The column index.
4183      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4184      */
4185     getRenderer : function(col){
4186         if(!this.config[col].renderer){
4187             return Roo.grid.ColumnModel.defaultRenderer;
4188         }
4189         return this.config[col].renderer;
4190     },
4191
4192     /**
4193      * Sets the rendering (formatting) function for a column.
4194      * @param {Number} col The column index
4195      * @param {Function} fn The function to use to process the cell's raw data
4196      * to return HTML markup for the grid view. The render function is called with
4197      * the following parameters:<ul>
4198      * <li>Data value.</li>
4199      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4200      * <li>css A CSS style string to apply to the table cell.</li>
4201      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4202      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4203      * <li>Row index</li>
4204      * <li>Column index</li>
4205      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4206      */
4207     setRenderer : function(col, fn){
4208         this.config[col].renderer = fn;
4209     },
4210
4211     /**
4212      * Returns the width for the specified column.
4213      * @param {Number} col The column index
4214      * @return {Number}
4215      */
4216     getColumnWidth : function(col){
4217         return this.config[col].width * 1 || this.defaultWidth;
4218     },
4219
4220     /**
4221      * Sets the width for a column.
4222      * @param {Number} col The column index
4223      * @param {Number} width The new width
4224      */
4225     setColumnWidth : function(col, width, suppressEvent){
4226         this.config[col].width = width;
4227         this.totalWidth = null;
4228         if(!suppressEvent){
4229              this.fireEvent("widthchange", this, col, width);
4230         }
4231     },
4232
4233     /**
4234      * Returns the total width of all columns.
4235      * @param {Boolean} includeHidden True to include hidden column widths
4236      * @return {Number}
4237      */
4238     getTotalWidth : function(includeHidden){
4239         if(!this.totalWidth){
4240             this.totalWidth = 0;
4241             for(var i = 0, len = this.config.length; i < len; i++){
4242                 if(includeHidden || !this.isHidden(i)){
4243                     this.totalWidth += this.getColumnWidth(i);
4244                 }
4245             }
4246         }
4247         return this.totalWidth;
4248     },
4249
4250     /**
4251      * Returns the header for the specified column.
4252      * @param {Number} col The column index
4253      * @return {String}
4254      */
4255     getColumnHeader : function(col){
4256         return this.config[col].header;
4257     },
4258
4259     /**
4260      * Sets the header for a column.
4261      * @param {Number} col The column index
4262      * @param {String} header The new header
4263      */
4264     setColumnHeader : function(col, header){
4265         this.config[col].header = header;
4266         this.fireEvent("headerchange", this, col, header);
4267     },
4268
4269     /**
4270      * Returns the tooltip for the specified column.
4271      * @param {Number} col The column index
4272      * @return {String}
4273      */
4274     getColumnTooltip : function(col){
4275             return this.config[col].tooltip;
4276     },
4277     /**
4278      * Sets the tooltip for a column.
4279      * @param {Number} col The column index
4280      * @param {String} tooltip The new tooltip
4281      */
4282     setColumnTooltip : function(col, tooltip){
4283             this.config[col].tooltip = tooltip;
4284     },
4285
4286     /**
4287      * Returns the dataIndex for the specified column.
4288      * @param {Number} col The column index
4289      * @return {Number}
4290      */
4291     getDataIndex : function(col){
4292         return this.config[col].dataIndex;
4293     },
4294
4295     /**
4296      * Sets the dataIndex for a column.
4297      * @param {Number} col The column index
4298      * @param {Number} dataIndex The new dataIndex
4299      */
4300     setDataIndex : function(col, dataIndex){
4301         this.config[col].dataIndex = dataIndex;
4302     },
4303
4304     
4305     
4306     /**
4307      * Returns true if the cell is editable.
4308      * @param {Number} colIndex The column index
4309      * @param {Number} rowIndex The row index
4310      * @return {Boolean}
4311      */
4312     isCellEditable : function(colIndex, rowIndex){
4313         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4314     },
4315
4316     /**
4317      * Returns the editor defined for the cell/column.
4318      * return false or null to disable editing.
4319      * @param {Number} colIndex The column index
4320      * @param {Number} rowIndex The row index
4321      * @return {Object}
4322      */
4323     getCellEditor : function(colIndex, rowIndex){
4324         return this.config[colIndex].editor;
4325     },
4326
4327     /**
4328      * Sets if a column is editable.
4329      * @param {Number} col The column index
4330      * @param {Boolean} editable True if the column is editable
4331      */
4332     setEditable : function(col, editable){
4333         this.config[col].editable = editable;
4334     },
4335
4336
4337     /**
4338      * Returns true if the column is hidden.
4339      * @param {Number} colIndex The column index
4340      * @return {Boolean}
4341      */
4342     isHidden : function(colIndex){
4343         return this.config[colIndex].hidden;
4344     },
4345
4346
4347     /**
4348      * Returns true if the column width cannot be changed
4349      */
4350     isFixed : function(colIndex){
4351         return this.config[colIndex].fixed;
4352     },
4353
4354     /**
4355      * Returns true if the column can be resized
4356      * @return {Boolean}
4357      */
4358     isResizable : function(colIndex){
4359         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4360     },
4361     /**
4362      * Sets if a column is hidden.
4363      * @param {Number} colIndex The column index
4364      * @param {Boolean} hidden True if the column is hidden
4365      */
4366     setHidden : function(colIndex, hidden){
4367         this.config[colIndex].hidden = hidden;
4368         this.totalWidth = null;
4369         this.fireEvent("hiddenchange", this, colIndex, hidden);
4370     },
4371
4372     /**
4373      * Sets the editor for a column.
4374      * @param {Number} col The column index
4375      * @param {Object} editor The editor object
4376      */
4377     setEditor : function(col, editor){
4378         this.config[col].editor = editor;
4379     }
4380 });
4381
4382 Roo.grid.ColumnModel.defaultRenderer = function(value){
4383         if(typeof value == "string" && value.length < 1){
4384             return "&#160;";
4385         }
4386         return value;
4387 };
4388
4389 // Alias for backwards compatibility
4390 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4391 /*
4392  * Based on:
4393  * Ext JS Library 1.1.1
4394  * Copyright(c) 2006-2007, Ext JS, LLC.
4395  *
4396  * Originally Released Under LGPL - original licence link has changed is not relivant.
4397  *
4398  * Fork - LGPL
4399  * <script type="text/javascript">
4400  */
4401  
4402 /**
4403  * @class Roo.LoadMask
4404  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4405  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4406  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4407  * element's UpdateManager load indicator and will be destroyed after the initial load.
4408  * @constructor
4409  * Create a new LoadMask
4410  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4411  * @param {Object} config The config object
4412  */
4413 Roo.LoadMask = function(el, config){
4414     this.el = Roo.get(el);
4415     Roo.apply(this, config);
4416     if(this.store){
4417         this.store.on('beforeload', this.onBeforeLoad, this);
4418         this.store.on('load', this.onLoad, this);
4419         this.store.on('loadexception', this.onLoadException, this);
4420         this.removeMask = false;
4421     }else{
4422         var um = this.el.getUpdateManager();
4423         um.showLoadIndicator = false; // disable the default indicator
4424         um.on('beforeupdate', this.onBeforeLoad, this);
4425         um.on('update', this.onLoad, this);
4426         um.on('failure', this.onLoad, this);
4427         this.removeMask = true;
4428     }
4429 };
4430
4431 Roo.LoadMask.prototype = {
4432     /**
4433      * @cfg {Boolean} removeMask
4434      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4435      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4436      */
4437     /**
4438      * @cfg {String} msg
4439      * The text to display in a centered loading message box (defaults to 'Loading...')
4440      */
4441     msg : 'Loading...',
4442     /**
4443      * @cfg {String} msgCls
4444      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4445      */
4446     msgCls : 'x-mask-loading',
4447
4448     /**
4449      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4450      * @type Boolean
4451      */
4452     disabled: false,
4453
4454     /**
4455      * Disables the mask to prevent it from being displayed
4456      */
4457     disable : function(){
4458        this.disabled = true;
4459     },
4460
4461     /**
4462      * Enables the mask so that it can be displayed
4463      */
4464     enable : function(){
4465         this.disabled = false;
4466     },
4467     
4468     onLoadException : function()
4469     {
4470         Roo.log(arguments);
4471         
4472         if (typeof(arguments[3]) != 'undefined') {
4473             Roo.MessageBox.alert("Error loading",arguments[3]);
4474         } 
4475         /*
4476         try {
4477             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4478                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4479             }   
4480         } catch(e) {
4481             
4482         }
4483         */
4484     
4485         
4486         
4487         this.el.unmask(this.removeMask);
4488     },
4489     // private
4490     onLoad : function()
4491     {
4492         this.el.unmask(this.removeMask);
4493     },
4494
4495     // private
4496     onBeforeLoad : function(){
4497         if(!this.disabled){
4498             this.el.mask(this.msg, this.msgCls);
4499         }
4500     },
4501
4502     // private
4503     destroy : function(){
4504         if(this.store){
4505             this.store.un('beforeload', this.onBeforeLoad, this);
4506             this.store.un('load', this.onLoad, this);
4507             this.store.un('loadexception', this.onLoadException, this);
4508         }else{
4509             var um = this.el.getUpdateManager();
4510             um.un('beforeupdate', this.onBeforeLoad, this);
4511             um.un('update', this.onLoad, this);
4512             um.un('failure', this.onLoad, this);
4513         }
4514     }
4515 };/*
4516  * - LGPL
4517  *
4518  * table
4519  * 
4520  */
4521
4522 /**
4523  * @class Roo.bootstrap.Table
4524  * @extends Roo.bootstrap.Component
4525  * Bootstrap Table class
4526  * @cfg {String} cls table class
4527  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4528  * @cfg {String} bgcolor Specifies the background color for a table
4529  * @cfg {Number} border Specifies whether the table cells should have borders or not
4530  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4531  * @cfg {Number} cellspacing Specifies the space between cells
4532  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4533  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4534  * @cfg {String} sortable Specifies that the table should be sortable
4535  * @cfg {String} summary Specifies a summary of the content of a table
4536  * @cfg {Number} width Specifies the width of a table
4537  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4538  * 
4539  * @cfg {boolean} striped Should the rows be alternative striped
4540  * @cfg {boolean} bordered Add borders to the table
4541  * @cfg {boolean} hover Add hover highlighting
4542  * @cfg {boolean} condensed Format condensed
4543  * @cfg {boolean} responsive Format condensed
4544  * @cfg {Boolean} loadMask (true|false) default false
4545  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4546  * @cfg {Boolean} thead (true|false) generate thead, default true
4547  * @cfg {Boolean} RowSelection (true|false) default false
4548  * @cfg {Boolean} CellSelection (true|false) default false
4549  *
4550  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4551  
4552  * 
4553  * @constructor
4554  * Create a new Table
4555  * @param {Object} config The config object
4556  */
4557
4558 Roo.bootstrap.Table = function(config){
4559     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4560     
4561     if (this.sm) {
4562         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4563         this.sm = this.selModel;
4564         this.sm.xmodule = this.xmodule || false;
4565     }
4566     if (this.cm && typeof(this.cm.config) == 'undefined') {
4567         this.colModel = new Roo.grid.ColumnModel(this.cm);
4568         this.cm = this.colModel;
4569         this.cm.xmodule = this.xmodule || false;
4570     }
4571     if (this.store) {
4572         this.store= Roo.factory(this.store, Roo.data);
4573         this.ds = this.store;
4574         this.ds.xmodule = this.xmodule || false;
4575          
4576     }
4577     if (this.footer && this.store) {
4578         this.footer.dataSource = this.ds;
4579         this.footer = Roo.factory(this.footer);
4580     }
4581     
4582     /** @private */
4583     this.addEvents({
4584         /**
4585          * @event cellclick
4586          * Fires when a cell is clicked
4587          * @param {Roo.bootstrap.Table} this
4588          * @param {Roo.Element} el
4589          * @param {Number} rowIndex
4590          * @param {Number} columnIndex
4591          * @param {Roo.EventObject} e
4592          */
4593         "cellclick" : true,
4594         /**
4595          * @event celldblclick
4596          * Fires when a cell is double clicked
4597          * @param {Roo.bootstrap.Table} this
4598          * @param {Roo.Element} el
4599          * @param {Number} rowIndex
4600          * @param {Number} columnIndex
4601          * @param {Roo.EventObject} e
4602          */
4603         "celldblclick" : true,
4604         /**
4605          * @event rowclick
4606          * Fires when a row is clicked
4607          * @param {Roo.bootstrap.Table} this
4608          * @param {Roo.Element} el
4609          * @param {Number} rowIndex
4610          * @param {Roo.EventObject} e
4611          */
4612         "rowclick" : true,
4613         /**
4614          * @event rowdblclick
4615          * Fires when a row is double clicked
4616          * @param {Roo.bootstrap.Table} this
4617          * @param {Roo.Element} el
4618          * @param {Number} rowIndex
4619          * @param {Roo.EventObject} e
4620          */
4621         "rowdblclick" : true,
4622         /**
4623          * @event mouseover
4624          * Fires when a mouseover occur
4625          * @param {Roo.bootstrap.Table} this
4626          * @param {Roo.Element} el
4627          * @param {Number} rowIndex
4628          * @param {Number} columnIndex
4629          * @param {Roo.EventObject} e
4630          */
4631         "mouseover" : true,
4632         /**
4633          * @event mouseout
4634          * Fires when a mouseout occur
4635          * @param {Roo.bootstrap.Table} this
4636          * @param {Roo.Element} el
4637          * @param {Number} rowIndex
4638          * @param {Number} columnIndex
4639          * @param {Roo.EventObject} e
4640          */
4641         "mouseout" : true,
4642         /**
4643          * @event rowclass
4644          * Fires when a row is rendered, so you can change add a style to it.
4645          * @param {Roo.bootstrap.Table} this
4646          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4647          */
4648         'rowclass' : true
4649         
4650     });
4651 };
4652
4653 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4654     
4655     cls: false,
4656     align: false,
4657     bgcolor: false,
4658     border: false,
4659     cellpadding: false,
4660     cellspacing: false,
4661     frame: false,
4662     rules: false,
4663     sortable: false,
4664     summary: false,
4665     width: false,
4666     striped : false,
4667     bordered: false,
4668     hover:  false,
4669     condensed : false,
4670     responsive : false,
4671     sm : false,
4672     cm : false,
4673     store : false,
4674     loadMask : false,
4675     tfoot : true,
4676     thead : true,
4677     RowSelection : false,
4678     CellSelection : false,
4679     layout : false,
4680     
4681     // Roo.Element - the tbody
4682     mainBody: false, 
4683     
4684     getAutoCreate : function(){
4685         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4686         
4687         cfg = {
4688             tag: 'table',
4689             cls : 'table',
4690             cn : []
4691         }
4692             
4693         if (this.striped) {
4694             cfg.cls += ' table-striped';
4695         }
4696         
4697         if (this.hover) {
4698             cfg.cls += ' table-hover';
4699         }
4700         if (this.bordered) {
4701             cfg.cls += ' table-bordered';
4702         }
4703         if (this.condensed) {
4704             cfg.cls += ' table-condensed';
4705         }
4706         if (this.responsive) {
4707             cfg.cls += ' table-responsive';
4708         }
4709         
4710         if (this.cls) {
4711             cfg.cls+=  ' ' +this.cls;
4712         }
4713         
4714         // this lot should be simplifed...
4715         
4716         if (this.align) {
4717             cfg.align=this.align;
4718         }
4719         if (this.bgcolor) {
4720             cfg.bgcolor=this.bgcolor;
4721         }
4722         if (this.border) {
4723             cfg.border=this.border;
4724         }
4725         if (this.cellpadding) {
4726             cfg.cellpadding=this.cellpadding;
4727         }
4728         if (this.cellspacing) {
4729             cfg.cellspacing=this.cellspacing;
4730         }
4731         if (this.frame) {
4732             cfg.frame=this.frame;
4733         }
4734         if (this.rules) {
4735             cfg.rules=this.rules;
4736         }
4737         if (this.sortable) {
4738             cfg.sortable=this.sortable;
4739         }
4740         if (this.summary) {
4741             cfg.summary=this.summary;
4742         }
4743         if (this.width) {
4744             cfg.width=this.width;
4745         }
4746         if (this.layout) {
4747             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4748         }
4749         
4750         if(this.store || this.cm){
4751             if(this.thead){
4752                 cfg.cn.push(this.renderHeader());
4753             }
4754             
4755             cfg.cn.push(this.renderBody());
4756             
4757             if(this.tfoot){
4758                 cfg.cn.push(this.renderFooter());
4759             }
4760             
4761             cfg.cls+=  ' TableGrid';
4762         }
4763         
4764         return { cn : [ cfg ] };
4765     },
4766     
4767     initEvents : function()
4768     {   
4769         if(!this.store || !this.cm){
4770             return;
4771         }
4772         
4773         //Roo.log('initEvents with ds!!!!');
4774         
4775         this.mainBody = this.el.select('tbody', true).first();
4776         
4777         
4778         var _this = this;
4779         
4780         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4781             e.on('click', _this.sort, _this);
4782         });
4783         
4784         this.el.on("click", this.onClick, this);
4785         this.el.on("dblclick", this.onDblClick, this);
4786         
4787         this.parent().el.setStyle('position', 'relative');
4788         if (this.footer) {
4789             this.footer.parentId = this.id;
4790             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
4791         }
4792         
4793         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
4794         
4795         this.store.on('load', this.onLoad, this);
4796         this.store.on('beforeload', this.onBeforeLoad, this);
4797         this.store.on('update', this.onUpdate, this);
4798         
4799     },
4800     
4801     onMouseover : function(e, el)
4802     {
4803         var cell = Roo.get(el);
4804         
4805         if(!cell){
4806             return;
4807         }
4808         
4809         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4810             cell = cell.findParent('td', false, true);
4811         }
4812         
4813         var row = cell.findParent('tr', false, true);
4814         var cellIndex = cell.dom.cellIndex;
4815         var rowIndex = row.dom.rowIndex - 1; // start from 0
4816         
4817         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
4818         
4819     },
4820     
4821     onMouseout : function(e, el)
4822     {
4823         var cell = Roo.get(el);
4824         
4825         if(!cell){
4826             return;
4827         }
4828         
4829         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4830             cell = cell.findParent('td', false, true);
4831         }
4832         
4833         var row = cell.findParent('tr', false, true);
4834         var cellIndex = cell.dom.cellIndex;
4835         var rowIndex = row.dom.rowIndex - 1; // start from 0
4836         
4837         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
4838         
4839     },
4840     
4841     onClick : function(e, el)
4842     {
4843         var cell = Roo.get(el);
4844         
4845         if(!cell || (!this.CellSelection && !this.RowSelection)){
4846             return;
4847         }
4848         
4849         
4850         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4851             cell = cell.findParent('td', false, true);
4852         }
4853         
4854         var row = cell.findParent('tr', false, true);
4855         var cellIndex = cell.dom.cellIndex;
4856         var rowIndex = row.dom.rowIndex - 1;
4857         
4858         if(this.CellSelection){
4859             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
4860         }
4861         
4862         if(this.RowSelection){
4863             this.fireEvent('rowclick', this, row, rowIndex, e);
4864         }
4865         
4866         
4867     },
4868     
4869     onDblClick : function(e,el)
4870     {
4871         var cell = Roo.get(el);
4872         
4873         if(!cell || (!this.CellSelection && !this.RowSelection)){
4874             return;
4875         }
4876         
4877         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4878             cell = cell.findParent('td', false, true);
4879         }
4880         
4881         var row = cell.findParent('tr', false, true);
4882         var cellIndex = cell.dom.cellIndex;
4883         var rowIndex = row.dom.rowIndex - 1;
4884         
4885         if(this.CellSelection){
4886             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
4887         }
4888         
4889         if(this.RowSelection){
4890             this.fireEvent('rowdblclick', this, row, rowIndex, e);
4891         }
4892     },
4893     
4894     sort : function(e,el)
4895     {
4896         var col = Roo.get(el)
4897         
4898         if(!col.hasClass('sortable')){
4899             return;
4900         }
4901         
4902         var sort = col.attr('sort');
4903         var dir = 'ASC';
4904         
4905         if(col.hasClass('glyphicon-arrow-up')){
4906             dir = 'DESC';
4907         }
4908         
4909         this.store.sortInfo = {field : sort, direction : dir};
4910         
4911         if (this.footer) {
4912             Roo.log("calling footer first");
4913             this.footer.onClick('first');
4914         } else {
4915         
4916             this.store.load({ params : { start : 0 } });
4917         }
4918     },
4919     
4920     renderHeader : function()
4921     {
4922         var header = {
4923             tag: 'thead',
4924             cn : []
4925         };
4926         
4927         var cm = this.cm;
4928         
4929         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4930             
4931             var config = cm.config[i];
4932                     
4933             var c = {
4934                 tag: 'th',
4935                 style : '',
4936                 html: cm.getColumnHeader(i)
4937             };
4938             
4939             if(typeof(config.hidden) != 'undefined' && config.hidden){
4940                 c.style += ' display:none;';
4941             }
4942             
4943             if(typeof(config.dataIndex) != 'undefined'){
4944                 c.sort = config.dataIndex;
4945             }
4946             
4947             if(typeof(config.sortable) != 'undefined' && config.sortable){
4948                 c.cls = 'sortable';
4949             }
4950             
4951 //            if(typeof(config.align) != 'undefined' && config.align.length){
4952 //                c.style += ' text-align:' + config.align + ';';
4953 //            }
4954             
4955             if(typeof(config.width) != 'undefined'){
4956                 c.style += ' width:' + config.width + 'px;';
4957             }
4958             
4959             header.cn.push(c)
4960         }
4961         
4962         return header;
4963     },
4964     
4965     renderBody : function()
4966     {
4967         var body = {
4968             tag: 'tbody',
4969             cn : [
4970                 {
4971                     tag: 'tr',
4972                     cn : [
4973                         {
4974                             tag : 'td',
4975                             colspan :  this.cm.getColumnCount()
4976                         }
4977                     ]
4978                 }
4979             ]
4980         };
4981         
4982         return body;
4983     },
4984     
4985     renderFooter : function()
4986     {
4987         var footer = {
4988             tag: 'tfoot',
4989             cn : [
4990                 {
4991                     tag: 'tr',
4992                     cn : [
4993                         {
4994                             tag : 'td',
4995                             colspan :  this.cm.getColumnCount()
4996                         }
4997                     ]
4998                 }
4999             ]
5000         };
5001         
5002         return footer;
5003     },
5004     
5005     
5006     
5007     onLoad : function()
5008     {
5009         Roo.log('ds onload');
5010         this.clear();
5011         
5012         var _this = this;
5013         var cm = this.cm;
5014         var ds = this.store;
5015         
5016         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5017             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5018             
5019             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5020                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5021             }
5022             
5023             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5024                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5025             }
5026         });
5027         
5028         var tbody =  this.mainBody;
5029         
5030         var renders = [];
5031                     
5032         if(ds.getCount() > 0){
5033             ds.data.each(function(d,rowIndex){
5034                 var row =  this.renderRow(cm, ds, rowIndex);
5035                 
5036                 tbody.createChild(row);
5037                 
5038                 var _this = this;
5039                 
5040                 if(row.renders.length){
5041                     Roo.each(row.renders, function(r){
5042                         _this.renderColumn(r);
5043                     })
5044                 }
5045                 
5046             }, this);
5047         }
5048         
5049         Roo.each(this.el.select('tbody td', true).elements, function(e){
5050             e.on('mouseover', _this.onMouseover, _this);
5051         });
5052         
5053         Roo.each(this.el.select('tbody td', true).elements, function(e){
5054             e.on('mouseout', _this.onMouseout, _this);
5055         });
5056
5057         //if(this.loadMask){
5058         //    this.maskEl.hide();
5059         //}
5060     },
5061     
5062     
5063     onUpdate : function(ds,record)
5064     {
5065         this.refreshRow(record);
5066     },
5067     onRemove : function(ds, record, index, isUpdate){
5068         if(isUpdate !== true){
5069             this.fireEvent("beforerowremoved", this, index, record);
5070         }
5071         var bt = this.mainBody.dom;
5072         if(bt.rows[index]){
5073             bt.removeChild(bt.rows[index]);
5074         }
5075         
5076         if(isUpdate !== true){
5077             //this.stripeRows(index);
5078             //this.syncRowHeights(index, index);
5079             //this.layout();
5080             this.fireEvent("rowremoved", this, index, record);
5081         }
5082     },
5083     
5084     
5085     refreshRow : function(record){
5086         var ds = this.store, index;
5087         if(typeof record == 'number'){
5088             index = record;
5089             record = ds.getAt(index);
5090         }else{
5091             index = ds.indexOf(record);
5092         }
5093         this.insertRow(ds, index, true);
5094         this.onRemove(ds, record, index+1, true);
5095         //this.syncRowHeights(index, index);
5096         //this.layout();
5097         this.fireEvent("rowupdated", this, index, record);
5098     },
5099     
5100     insertRow : function(dm, rowIndex, isUpdate){
5101         
5102         if(!isUpdate){
5103             this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
5104         }
5105             //var s = this.getScrollState();
5106         var row = this.renderRow(this.cm, this.store, rowIndex);
5107         // insert before rowIndex..
5108         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5109         Roo.log(e);
5110             
5111         if(!isUpdate){
5112             this.fireEvent("rowsinserted", this, firstRow, lastRow);
5113             //this.syncRowHeights(firstRow, lastRow);
5114             //this.stripeRows(firstRow);
5115             //this.layout();
5116         }
5117         
5118     },
5119     
5120     
5121     getRowDom : function(rowIndex)
5122     {
5123         // not sure if I need to check this.. but let's do it anyway..
5124         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5125                 this.mainBody.dom.rows[rowIndex] : false
5126     },
5127     // returns the object tree for a tr..
5128   
5129     
5130     renderRow : function(cm, ds, rowIndex) {
5131         
5132         var d = ds.getAt(rowIndex);
5133         
5134         var row = {
5135             tag : 'tr',
5136             cn : []
5137         };
5138             
5139         var renders = [];
5140         
5141         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5142             var config = cm.config[i];
5143             
5144             var renderer = cm.getRenderer(i);
5145             var value = '';
5146             var id = Roo.id();
5147             
5148             if(typeof(renderer) !== 'undefined'){
5149                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5150             }
5151             
5152             if(typeof(value) === 'object'){
5153                 renders.push({
5154                     container : id,
5155                     cfg : value 
5156                 })
5157             }
5158             
5159             var rowcfg = {
5160                 record: d,
5161                 rowIndex : rowIndex,
5162                 colIndex : i,
5163                 rowClass : ''
5164             }
5165
5166             this.fireEvent('rowclass', this, rowcfg);
5167             
5168             var td = {
5169                 tag: 'td',
5170                 id: id,
5171                 cls : rowcfg.rowClass,
5172                 style: '',
5173                 html: (typeof(value) === 'object') ? '' : value
5174             };
5175             
5176             if(typeof(config.hidden) != 'undefined' && config.hidden){
5177                 td.style += ' display:none;';
5178             }
5179             
5180             if(typeof(config.align) != 'undefined' && config.align.length){
5181                 td.style += ' text-align:' + config.align + ';';
5182             }
5183             
5184             if(typeof(config.width) != 'undefined'){
5185                 td.style += ' width:' +  config.width + 'px;';
5186             }
5187              
5188             row.cn.push(td);
5189            
5190         }
5191         
5192         row.renders = renders;
5193         
5194         return row;
5195           
5196     },
5197     
5198     
5199     
5200     onBeforeLoad : function()
5201     {
5202         //Roo.log('ds onBeforeLoad');
5203         
5204         //this.clear();
5205         
5206         //if(this.loadMask){
5207         //    this.maskEl.show();
5208         //}
5209     },
5210     
5211     clear : function()
5212     {
5213         this.el.select('tbody', true).first().dom.innerHTML = '';
5214     },
5215     
5216     getSelectionModel : function(){
5217         if(!this.selModel){
5218             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5219         }
5220         return this.selModel;
5221     },
5222     
5223     renderColumn : function(r)
5224     {
5225         var _this = this;
5226         
5227         var t = r.cfg.render(r.container);
5228         
5229         if(r.cfg.cn){
5230             Roo.each(r.cfg.cn, function(c){
5231                 var child = {
5232                     container: t.getChildContainer(),
5233                     cfg: c
5234                 }
5235                 _this.renderColumn(child);
5236             })
5237         }
5238     }
5239    
5240 });
5241
5242  
5243
5244  /*
5245  * - LGPL
5246  *
5247  * table cell
5248  * 
5249  */
5250
5251 /**
5252  * @class Roo.bootstrap.TableCell
5253  * @extends Roo.bootstrap.Component
5254  * Bootstrap TableCell class
5255  * @cfg {String} html cell contain text
5256  * @cfg {String} cls cell class
5257  * @cfg {String} tag cell tag (td|th) default td
5258  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5259  * @cfg {String} align Aligns the content in a cell
5260  * @cfg {String} axis Categorizes cells
5261  * @cfg {String} bgcolor Specifies the background color of a cell
5262  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5263  * @cfg {Number} colspan Specifies the number of columns a cell should span
5264  * @cfg {String} headers Specifies one or more header cells a cell is related to
5265  * @cfg {Number} height Sets the height of a cell
5266  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5267  * @cfg {Number} rowspan Sets the number of rows a cell should span
5268  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5269  * @cfg {String} valign Vertical aligns the content in a cell
5270  * @cfg {Number} width Specifies the width of a cell
5271  * 
5272  * @constructor
5273  * Create a new TableCell
5274  * @param {Object} config The config object
5275  */
5276
5277 Roo.bootstrap.TableCell = function(config){
5278     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5279 };
5280
5281 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5282     
5283     html: false,
5284     cls: false,
5285     tag: false,
5286     abbr: false,
5287     align: false,
5288     axis: false,
5289     bgcolor: false,
5290     charoff: false,
5291     colspan: false,
5292     headers: false,
5293     height: false,
5294     nowrap: false,
5295     rowspan: false,
5296     scope: false,
5297     valign: false,
5298     width: false,
5299     
5300     
5301     getAutoCreate : function(){
5302         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5303         
5304         cfg = {
5305             tag: 'td'
5306         }
5307         
5308         if(this.tag){
5309             cfg.tag = this.tag;
5310         }
5311         
5312         if (this.html) {
5313             cfg.html=this.html
5314         }
5315         if (this.cls) {
5316             cfg.cls=this.cls
5317         }
5318         if (this.abbr) {
5319             cfg.abbr=this.abbr
5320         }
5321         if (this.align) {
5322             cfg.align=this.align
5323         }
5324         if (this.axis) {
5325             cfg.axis=this.axis
5326         }
5327         if (this.bgcolor) {
5328             cfg.bgcolor=this.bgcolor
5329         }
5330         if (this.charoff) {
5331             cfg.charoff=this.charoff
5332         }
5333         if (this.colspan) {
5334             cfg.colspan=this.colspan
5335         }
5336         if (this.headers) {
5337             cfg.headers=this.headers
5338         }
5339         if (this.height) {
5340             cfg.height=this.height
5341         }
5342         if (this.nowrap) {
5343             cfg.nowrap=this.nowrap
5344         }
5345         if (this.rowspan) {
5346             cfg.rowspan=this.rowspan
5347         }
5348         if (this.scope) {
5349             cfg.scope=this.scope
5350         }
5351         if (this.valign) {
5352             cfg.valign=this.valign
5353         }
5354         if (this.width) {
5355             cfg.width=this.width
5356         }
5357         
5358         
5359         return cfg;
5360     }
5361    
5362 });
5363
5364  
5365
5366  /*
5367  * - LGPL
5368  *
5369  * table row
5370  * 
5371  */
5372
5373 /**
5374  * @class Roo.bootstrap.TableRow
5375  * @extends Roo.bootstrap.Component
5376  * Bootstrap TableRow class
5377  * @cfg {String} cls row class
5378  * @cfg {String} align Aligns the content in a table row
5379  * @cfg {String} bgcolor Specifies a background color for a table row
5380  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5381  * @cfg {String} valign Vertical aligns the content in a table row
5382  * 
5383  * @constructor
5384  * Create a new TableRow
5385  * @param {Object} config The config object
5386  */
5387
5388 Roo.bootstrap.TableRow = function(config){
5389     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5390 };
5391
5392 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5393     
5394     cls: false,
5395     align: false,
5396     bgcolor: false,
5397     charoff: false,
5398     valign: false,
5399     
5400     getAutoCreate : function(){
5401         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5402         
5403         cfg = {
5404             tag: 'tr'
5405         }
5406             
5407         if(this.cls){
5408             cfg.cls = this.cls;
5409         }
5410         if(this.align){
5411             cfg.align = this.align;
5412         }
5413         if(this.bgcolor){
5414             cfg.bgcolor = this.bgcolor;
5415         }
5416         if(this.charoff){
5417             cfg.charoff = this.charoff;
5418         }
5419         if(this.valign){
5420             cfg.valign = this.valign;
5421         }
5422         
5423         return cfg;
5424     }
5425    
5426 });
5427
5428  
5429
5430  /*
5431  * - LGPL
5432  *
5433  * table body
5434  * 
5435  */
5436
5437 /**
5438  * @class Roo.bootstrap.TableBody
5439  * @extends Roo.bootstrap.Component
5440  * Bootstrap TableBody class
5441  * @cfg {String} cls element class
5442  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5443  * @cfg {String} align Aligns the content inside the element
5444  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5445  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5446  * 
5447  * @constructor
5448  * Create a new TableBody
5449  * @param {Object} config The config object
5450  */
5451
5452 Roo.bootstrap.TableBody = function(config){
5453     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5454 };
5455
5456 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5457     
5458     cls: false,
5459     tag: false,
5460     align: false,
5461     charoff: false,
5462     valign: false,
5463     
5464     getAutoCreate : function(){
5465         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5466         
5467         cfg = {
5468             tag: 'tbody'
5469         }
5470             
5471         if (this.cls) {
5472             cfg.cls=this.cls
5473         }
5474         if(this.tag){
5475             cfg.tag = this.tag;
5476         }
5477         
5478         if(this.align){
5479             cfg.align = this.align;
5480         }
5481         if(this.charoff){
5482             cfg.charoff = this.charoff;
5483         }
5484         if(this.valign){
5485             cfg.valign = this.valign;
5486         }
5487         
5488         return cfg;
5489     }
5490     
5491     
5492 //    initEvents : function()
5493 //    {
5494 //        
5495 //        if(!this.store){
5496 //            return;
5497 //        }
5498 //        
5499 //        this.store = Roo.factory(this.store, Roo.data);
5500 //        this.store.on('load', this.onLoad, this);
5501 //        
5502 //        this.store.load();
5503 //        
5504 //    },
5505 //    
5506 //    onLoad: function () 
5507 //    {   
5508 //        this.fireEvent('load', this);
5509 //    }
5510 //    
5511 //   
5512 });
5513
5514  
5515
5516  /*
5517  * Based on:
5518  * Ext JS Library 1.1.1
5519  * Copyright(c) 2006-2007, Ext JS, LLC.
5520  *
5521  * Originally Released Under LGPL - original licence link has changed is not relivant.
5522  *
5523  * Fork - LGPL
5524  * <script type="text/javascript">
5525  */
5526
5527 // as we use this in bootstrap.
5528 Roo.namespace('Roo.form');
5529  /**
5530  * @class Roo.form.Action
5531  * Internal Class used to handle form actions
5532  * @constructor
5533  * @param {Roo.form.BasicForm} el The form element or its id
5534  * @param {Object} config Configuration options
5535  */
5536
5537  
5538  
5539 // define the action interface
5540 Roo.form.Action = function(form, options){
5541     this.form = form;
5542     this.options = options || {};
5543 };
5544 /**
5545  * Client Validation Failed
5546  * @const 
5547  */
5548 Roo.form.Action.CLIENT_INVALID = 'client';
5549 /**
5550  * Server Validation Failed
5551  * @const 
5552  */
5553 Roo.form.Action.SERVER_INVALID = 'server';
5554  /**
5555  * Connect to Server Failed
5556  * @const 
5557  */
5558 Roo.form.Action.CONNECT_FAILURE = 'connect';
5559 /**
5560  * Reading Data from Server Failed
5561  * @const 
5562  */
5563 Roo.form.Action.LOAD_FAILURE = 'load';
5564
5565 Roo.form.Action.prototype = {
5566     type : 'default',
5567     failureType : undefined,
5568     response : undefined,
5569     result : undefined,
5570
5571     // interface method
5572     run : function(options){
5573
5574     },
5575
5576     // interface method
5577     success : function(response){
5578
5579     },
5580
5581     // interface method
5582     handleResponse : function(response){
5583
5584     },
5585
5586     // default connection failure
5587     failure : function(response){
5588         
5589         this.response = response;
5590         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5591         this.form.afterAction(this, false);
5592     },
5593
5594     processResponse : function(response){
5595         this.response = response;
5596         if(!response.responseText){
5597             return true;
5598         }
5599         this.result = this.handleResponse(response);
5600         return this.result;
5601     },
5602
5603     // utility functions used internally
5604     getUrl : function(appendParams){
5605         var url = this.options.url || this.form.url || this.form.el.dom.action;
5606         if(appendParams){
5607             var p = this.getParams();
5608             if(p){
5609                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5610             }
5611         }
5612         return url;
5613     },
5614
5615     getMethod : function(){
5616         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5617     },
5618
5619     getParams : function(){
5620         var bp = this.form.baseParams;
5621         var p = this.options.params;
5622         if(p){
5623             if(typeof p == "object"){
5624                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5625             }else if(typeof p == 'string' && bp){
5626                 p += '&' + Roo.urlEncode(bp);
5627             }
5628         }else if(bp){
5629             p = Roo.urlEncode(bp);
5630         }
5631         return p;
5632     },
5633
5634     createCallback : function(){
5635         return {
5636             success: this.success,
5637             failure: this.failure,
5638             scope: this,
5639             timeout: (this.form.timeout*1000),
5640             upload: this.form.fileUpload ? this.success : undefined
5641         };
5642     }
5643 };
5644
5645 Roo.form.Action.Submit = function(form, options){
5646     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5647 };
5648
5649 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5650     type : 'submit',
5651
5652     haveProgress : false,
5653     uploadComplete : false,
5654     
5655     // uploadProgress indicator.
5656     uploadProgress : function()
5657     {
5658         if (!this.form.progressUrl) {
5659             return;
5660         }
5661         
5662         if (!this.haveProgress) {
5663             Roo.MessageBox.progress("Uploading", "Uploading");
5664         }
5665         if (this.uploadComplete) {
5666            Roo.MessageBox.hide();
5667            return;
5668         }
5669         
5670         this.haveProgress = true;
5671    
5672         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5673         
5674         var c = new Roo.data.Connection();
5675         c.request({
5676             url : this.form.progressUrl,
5677             params: {
5678                 id : uid
5679             },
5680             method: 'GET',
5681             success : function(req){
5682                //console.log(data);
5683                 var rdata = false;
5684                 var edata;
5685                 try  {
5686                    rdata = Roo.decode(req.responseText)
5687                 } catch (e) {
5688                     Roo.log("Invalid data from server..");
5689                     Roo.log(edata);
5690                     return;
5691                 }
5692                 if (!rdata || !rdata.success) {
5693                     Roo.log(rdata);
5694                     Roo.MessageBox.alert(Roo.encode(rdata));
5695                     return;
5696                 }
5697                 var data = rdata.data;
5698                 
5699                 if (this.uploadComplete) {
5700                    Roo.MessageBox.hide();
5701                    return;
5702                 }
5703                    
5704                 if (data){
5705                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5706                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5707                     );
5708                 }
5709                 this.uploadProgress.defer(2000,this);
5710             },
5711        
5712             failure: function(data) {
5713                 Roo.log('progress url failed ');
5714                 Roo.log(data);
5715             },
5716             scope : this
5717         });
5718            
5719     },
5720     
5721     
5722     run : function()
5723     {
5724         // run get Values on the form, so it syncs any secondary forms.
5725         this.form.getValues();
5726         
5727         var o = this.options;
5728         var method = this.getMethod();
5729         var isPost = method == 'POST';
5730         if(o.clientValidation === false || this.form.isValid()){
5731             
5732             if (this.form.progressUrl) {
5733                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5734                     (new Date() * 1) + '' + Math.random());
5735                     
5736             } 
5737             
5738             
5739             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5740                 form:this.form.el.dom,
5741                 url:this.getUrl(!isPost),
5742                 method: method,
5743                 params:isPost ? this.getParams() : null,
5744                 isUpload: this.form.fileUpload
5745             }));
5746             
5747             this.uploadProgress();
5748
5749         }else if (o.clientValidation !== false){ // client validation failed
5750             this.failureType = Roo.form.Action.CLIENT_INVALID;
5751             this.form.afterAction(this, false);
5752         }
5753     },
5754
5755     success : function(response)
5756     {
5757         this.uploadComplete= true;
5758         if (this.haveProgress) {
5759             Roo.MessageBox.hide();
5760         }
5761         
5762         
5763         var result = this.processResponse(response);
5764         if(result === true || result.success){
5765             this.form.afterAction(this, true);
5766             return;
5767         }
5768         if(result.errors){
5769             this.form.markInvalid(result.errors);
5770             this.failureType = Roo.form.Action.SERVER_INVALID;
5771         }
5772         this.form.afterAction(this, false);
5773     },
5774     failure : function(response)
5775     {
5776         this.uploadComplete= true;
5777         if (this.haveProgress) {
5778             Roo.MessageBox.hide();
5779         }
5780         
5781         this.response = response;
5782         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5783         this.form.afterAction(this, false);
5784     },
5785     
5786     handleResponse : function(response){
5787         if(this.form.errorReader){
5788             var rs = this.form.errorReader.read(response);
5789             var errors = [];
5790             if(rs.records){
5791                 for(var i = 0, len = rs.records.length; i < len; i++) {
5792                     var r = rs.records[i];
5793                     errors[i] = r.data;
5794                 }
5795             }
5796             if(errors.length < 1){
5797                 errors = null;
5798             }
5799             return {
5800                 success : rs.success,
5801                 errors : errors
5802             };
5803         }
5804         var ret = false;
5805         try {
5806             ret = Roo.decode(response.responseText);
5807         } catch (e) {
5808             ret = {
5809                 success: false,
5810                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5811                 errors : []
5812             };
5813         }
5814         return ret;
5815         
5816     }
5817 });
5818
5819
5820 Roo.form.Action.Load = function(form, options){
5821     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5822     this.reader = this.form.reader;
5823 };
5824
5825 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5826     type : 'load',
5827
5828     run : function(){
5829         
5830         Roo.Ajax.request(Roo.apply(
5831                 this.createCallback(), {
5832                     method:this.getMethod(),
5833                     url:this.getUrl(false),
5834                     params:this.getParams()
5835         }));
5836     },
5837
5838     success : function(response){
5839         
5840         var result = this.processResponse(response);
5841         if(result === true || !result.success || !result.data){
5842             this.failureType = Roo.form.Action.LOAD_FAILURE;
5843             this.form.afterAction(this, false);
5844             return;
5845         }
5846         this.form.clearInvalid();
5847         this.form.setValues(result.data);
5848         this.form.afterAction(this, true);
5849     },
5850
5851     handleResponse : function(response){
5852         if(this.form.reader){
5853             var rs = this.form.reader.read(response);
5854             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5855             return {
5856                 success : rs.success,
5857                 data : data
5858             };
5859         }
5860         return Roo.decode(response.responseText);
5861     }
5862 });
5863
5864 Roo.form.Action.ACTION_TYPES = {
5865     'load' : Roo.form.Action.Load,
5866     'submit' : Roo.form.Action.Submit
5867 };/*
5868  * - LGPL
5869  *
5870  * form
5871  * 
5872  */
5873
5874 /**
5875  * @class Roo.bootstrap.Form
5876  * @extends Roo.bootstrap.Component
5877  * Bootstrap Form class
5878  * @cfg {String} method  GET | POST (default POST)
5879  * @cfg {String} labelAlign top | left (default top)
5880   * @cfg {String} align left  | right - for navbars
5881
5882  * 
5883  * @constructor
5884  * Create a new Form
5885  * @param {Object} config The config object
5886  */
5887
5888
5889 Roo.bootstrap.Form = function(config){
5890     Roo.bootstrap.Form.superclass.constructor.call(this, config);
5891     this.addEvents({
5892         /**
5893          * @event clientvalidation
5894          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5895          * @param {Form} this
5896          * @param {Boolean} valid true if the form has passed client-side validation
5897          */
5898         clientvalidation: true,
5899         /**
5900          * @event beforeaction
5901          * Fires before any action is performed. Return false to cancel the action.
5902          * @param {Form} this
5903          * @param {Action} action The action to be performed
5904          */
5905         beforeaction: true,
5906         /**
5907          * @event actionfailed
5908          * Fires when an action fails.
5909          * @param {Form} this
5910          * @param {Action} action The action that failed
5911          */
5912         actionfailed : true,
5913         /**
5914          * @event actioncomplete
5915          * Fires when an action is completed.
5916          * @param {Form} this
5917          * @param {Action} action The action that completed
5918          */
5919         actioncomplete : true
5920     });
5921     
5922 };
5923
5924 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
5925       
5926      /**
5927      * @cfg {String} method
5928      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
5929      */
5930     method : 'POST',
5931     /**
5932      * @cfg {String} url
5933      * The URL to use for form actions if one isn't supplied in the action options.
5934      */
5935     /**
5936      * @cfg {Boolean} fileUpload
5937      * Set to true if this form is a file upload.
5938      */
5939      
5940     /**
5941      * @cfg {Object} baseParams
5942      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
5943      */
5944       
5945     /**
5946      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5947      */
5948     timeout: 30,
5949     /**
5950      * @cfg {Sting} align (left|right) for navbar forms
5951      */
5952     align : 'left',
5953
5954     // private
5955     activeAction : null,
5956  
5957     /**
5958      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5959      * element by passing it or its id or mask the form itself by passing in true.
5960      * @type Mixed
5961      */
5962     waitMsgTarget : false,
5963     
5964      
5965     
5966     /**
5967      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5968      * element by passing it or its id or mask the form itself by passing in true.
5969      * @type Mixed
5970      */
5971     
5972     getAutoCreate : function(){
5973         
5974         var cfg = {
5975             tag: 'form',
5976             method : this.method || 'POST',
5977             id : this.id || Roo.id(),
5978             cls : ''
5979         }
5980         if (this.parent().xtype.match(/^Nav/)) {
5981             cfg.cls = 'navbar-form navbar-' + this.align;
5982             
5983         }
5984         
5985         if (this.labelAlign == 'left' ) {
5986             cfg.cls += ' form-horizontal';
5987         }
5988         
5989         
5990         return cfg;
5991     },
5992     initEvents : function()
5993     {
5994         this.el.on('submit', this.onSubmit, this);
5995         // this was added as random key presses on the form where triggering form submit.
5996         this.el.on('keypress', function(e) {
5997             if (e.getCharCode() != 13) {
5998                 return true;
5999             }
6000             // we might need to allow it for textareas.. and some other items.
6001             // check e.getTarget().
6002             
6003             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6004                 return true;
6005             }
6006         
6007             Roo.log("keypress blocked");
6008             
6009             e.preventDefault();
6010             return false;
6011         });
6012         
6013     },
6014     // private
6015     onSubmit : function(e){
6016         e.stopEvent();
6017     },
6018     
6019      /**
6020      * Returns true if client-side validation on the form is successful.
6021      * @return Boolean
6022      */
6023     isValid : function(){
6024         var items = this.getItems();
6025         var valid = true;
6026         items.each(function(f){
6027            if(!f.validate()){
6028                valid = false;
6029                
6030            }
6031         });
6032         return valid;
6033     },
6034     /**
6035      * Returns true if any fields in this form have changed since their original load.
6036      * @return Boolean
6037      */
6038     isDirty : function(){
6039         var dirty = false;
6040         var items = this.getItems();
6041         items.each(function(f){
6042            if(f.isDirty()){
6043                dirty = true;
6044                return false;
6045            }
6046            return true;
6047         });
6048         return dirty;
6049     },
6050      /**
6051      * Performs a predefined action (submit or load) or custom actions you define on this form.
6052      * @param {String} actionName The name of the action type
6053      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6054      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6055      * accept other config options):
6056      * <pre>
6057 Property          Type             Description
6058 ----------------  ---------------  ----------------------------------------------------------------------------------
6059 url               String           The url for the action (defaults to the form's url)
6060 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6061 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6062 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6063                                    validate the form on the client (defaults to false)
6064      * </pre>
6065      * @return {BasicForm} this
6066      */
6067     doAction : function(action, options){
6068         if(typeof action == 'string'){
6069             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6070         }
6071         if(this.fireEvent('beforeaction', this, action) !== false){
6072             this.beforeAction(action);
6073             action.run.defer(100, action);
6074         }
6075         return this;
6076     },
6077     
6078     // private
6079     beforeAction : function(action){
6080         var o = action.options;
6081         
6082         // not really supported yet.. ??
6083         
6084         //if(this.waitMsgTarget === true){
6085             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6086         //}else if(this.waitMsgTarget){
6087         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6088         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6089         //}else {
6090         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6091        // }
6092          
6093     },
6094
6095     // private
6096     afterAction : function(action, success){
6097         this.activeAction = null;
6098         var o = action.options;
6099         
6100         //if(this.waitMsgTarget === true){
6101             this.el.unmask();
6102         //}else if(this.waitMsgTarget){
6103         //    this.waitMsgTarget.unmask();
6104         //}else{
6105         //    Roo.MessageBox.updateProgress(1);
6106         //    Roo.MessageBox.hide();
6107        // }
6108         // 
6109         if(success){
6110             if(o.reset){
6111                 this.reset();
6112             }
6113             Roo.callback(o.success, o.scope, [this, action]);
6114             this.fireEvent('actioncomplete', this, action);
6115             
6116         }else{
6117             
6118             // failure condition..
6119             // we have a scenario where updates need confirming.
6120             // eg. if a locking scenario exists..
6121             // we look for { errors : { needs_confirm : true }} in the response.
6122             if (
6123                 (typeof(action.result) != 'undefined')  &&
6124                 (typeof(action.result.errors) != 'undefined')  &&
6125                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6126            ){
6127                 var _t = this;
6128                 Roo.log("not supported yet");
6129                  /*
6130                 
6131                 Roo.MessageBox.confirm(
6132                     "Change requires confirmation",
6133                     action.result.errorMsg,
6134                     function(r) {
6135                         if (r != 'yes') {
6136                             return;
6137                         }
6138                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6139                     }
6140                     
6141                 );
6142                 */
6143                 
6144                 
6145                 return;
6146             }
6147             
6148             Roo.callback(o.failure, o.scope, [this, action]);
6149             // show an error message if no failed handler is set..
6150             if (!this.hasListener('actionfailed')) {
6151                 Roo.log("need to add dialog support");
6152                 /*
6153                 Roo.MessageBox.alert("Error",
6154                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6155                         action.result.errorMsg :
6156                         "Saving Failed, please check your entries or try again"
6157                 );
6158                 */
6159             }
6160             
6161             this.fireEvent('actionfailed', this, action);
6162         }
6163         
6164     },
6165     /**
6166      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6167      * @param {String} id The value to search for
6168      * @return Field
6169      */
6170     findField : function(id){
6171         var items = this.getItems();
6172         var field = items.get(id);
6173         if(!field){
6174              items.each(function(f){
6175                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6176                     field = f;
6177                     return false;
6178                 }
6179                 return true;
6180             });
6181         }
6182         return field || null;
6183     },
6184      /**
6185      * Mark fields in this form invalid in bulk.
6186      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6187      * @return {BasicForm} this
6188      */
6189     markInvalid : function(errors){
6190         if(errors instanceof Array){
6191             for(var i = 0, len = errors.length; i < len; i++){
6192                 var fieldError = errors[i];
6193                 var f = this.findField(fieldError.id);
6194                 if(f){
6195                     f.markInvalid(fieldError.msg);
6196                 }
6197             }
6198         }else{
6199             var field, id;
6200             for(id in errors){
6201                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6202                     field.markInvalid(errors[id]);
6203                 }
6204             }
6205         }
6206         //Roo.each(this.childForms || [], function (f) {
6207         //    f.markInvalid(errors);
6208         //});
6209         
6210         return this;
6211     },
6212
6213     /**
6214      * Set values for fields in this form in bulk.
6215      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6216      * @return {BasicForm} this
6217      */
6218     setValues : function(values){
6219         if(values instanceof Array){ // array of objects
6220             for(var i = 0, len = values.length; i < len; i++){
6221                 var v = values[i];
6222                 var f = this.findField(v.id);
6223                 if(f){
6224                     f.setValue(v.value);
6225                     if(this.trackResetOnLoad){
6226                         f.originalValue = f.getValue();
6227                     }
6228                 }
6229             }
6230         }else{ // object hash
6231             var field, id;
6232             for(id in values){
6233                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6234                     
6235                     if (field.setFromData && 
6236                         field.valueField && 
6237                         field.displayField &&
6238                         // combos' with local stores can 
6239                         // be queried via setValue()
6240                         // to set their value..
6241                         (field.store && !field.store.isLocal)
6242                         ) {
6243                         // it's a combo
6244                         var sd = { };
6245                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6246                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6247                         field.setFromData(sd);
6248                         
6249                     } else {
6250                         field.setValue(values[id]);
6251                     }
6252                     
6253                     
6254                     if(this.trackResetOnLoad){
6255                         field.originalValue = field.getValue();
6256                     }
6257                 }
6258             }
6259         }
6260          
6261         //Roo.each(this.childForms || [], function (f) {
6262         //    f.setValues(values);
6263         //});
6264                 
6265         return this;
6266     },
6267
6268     /**
6269      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6270      * they are returned as an array.
6271      * @param {Boolean} asString
6272      * @return {Object}
6273      */
6274     getValues : function(asString){
6275         //if (this.childForms) {
6276             // copy values from the child forms
6277         //    Roo.each(this.childForms, function (f) {
6278         //        this.setValues(f.getValues());
6279         //    }, this);
6280         //}
6281         
6282         
6283         
6284         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6285         if(asString === true){
6286             return fs;
6287         }
6288         return Roo.urlDecode(fs);
6289     },
6290     
6291     /**
6292      * Returns the fields in this form as an object with key/value pairs. 
6293      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6294      * @return {Object}
6295      */
6296     getFieldValues : function(with_hidden)
6297     {
6298         var items = this.getItems();
6299         var ret = {};
6300         items.each(function(f){
6301             if (!f.getName()) {
6302                 return;
6303             }
6304             var v = f.getValue();
6305             if (f.inputType =='radio') {
6306                 if (typeof(ret[f.getName()]) == 'undefined') {
6307                     ret[f.getName()] = ''; // empty..
6308                 }
6309                 
6310                 if (!f.el.dom.checked) {
6311                     return;
6312                     
6313                 }
6314                 v = f.el.dom.value;
6315                 
6316             }
6317             
6318             // not sure if this supported any more..
6319             if ((typeof(v) == 'object') && f.getRawValue) {
6320                 v = f.getRawValue() ; // dates..
6321             }
6322             // combo boxes where name != hiddenName...
6323             if (f.name != f.getName()) {
6324                 ret[f.name] = f.getRawValue();
6325             }
6326             ret[f.getName()] = v;
6327         });
6328         
6329         return ret;
6330     },
6331
6332     /**
6333      * Clears all invalid messages in this form.
6334      * @return {BasicForm} this
6335      */
6336     clearInvalid : function(){
6337         var items = this.getItems();
6338         
6339         items.each(function(f){
6340            f.clearInvalid();
6341         });
6342         
6343         
6344         
6345         return this;
6346     },
6347
6348     /**
6349      * Resets this form.
6350      * @return {BasicForm} this
6351      */
6352     reset : function(){
6353         var items = this.getItems();
6354         items.each(function(f){
6355             f.reset();
6356         });
6357         
6358         Roo.each(this.childForms || [], function (f) {
6359             f.reset();
6360         });
6361        
6362         
6363         return this;
6364     },
6365     getItems : function()
6366     {
6367         var r=new Roo.util.MixedCollection(false, function(o){
6368             return o.id || (o.id = Roo.id());
6369         });
6370         var iter = function(el) {
6371             if (el.inputEl) {
6372                 r.add(el);
6373             }
6374             if (!el.items) {
6375                 return;
6376             }
6377             Roo.each(el.items,function(e) {
6378                 iter(e);
6379             });
6380             
6381             
6382         };
6383         iter(this);
6384         return r;
6385         
6386         
6387         
6388         
6389     }
6390     
6391 });
6392
6393  
6394 /*
6395  * Based on:
6396  * Ext JS Library 1.1.1
6397  * Copyright(c) 2006-2007, Ext JS, LLC.
6398  *
6399  * Originally Released Under LGPL - original licence link has changed is not relivant.
6400  *
6401  * Fork - LGPL
6402  * <script type="text/javascript">
6403  */
6404 /**
6405  * @class Roo.form.VTypes
6406  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6407  * @singleton
6408  */
6409 Roo.form.VTypes = function(){
6410     // closure these in so they are only created once.
6411     var alpha = /^[a-zA-Z_]+$/;
6412     var alphanum = /^[a-zA-Z0-9_]+$/;
6413     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6414     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6415
6416     // All these messages and functions are configurable
6417     return {
6418         /**
6419          * The function used to validate email addresses
6420          * @param {String} value The email address
6421          */
6422         'email' : function(v){
6423             return email.test(v);
6424         },
6425         /**
6426          * The error text to display when the email validation function returns false
6427          * @type String
6428          */
6429         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6430         /**
6431          * The keystroke filter mask to be applied on email input
6432          * @type RegExp
6433          */
6434         'emailMask' : /[a-z0-9_\.\-@]/i,
6435
6436         /**
6437          * The function used to validate URLs
6438          * @param {String} value The URL
6439          */
6440         'url' : function(v){
6441             return url.test(v);
6442         },
6443         /**
6444          * The error text to display when the url validation function returns false
6445          * @type String
6446          */
6447         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6448         
6449         /**
6450          * The function used to validate alpha values
6451          * @param {String} value The value
6452          */
6453         'alpha' : function(v){
6454             return alpha.test(v);
6455         },
6456         /**
6457          * The error text to display when the alpha validation function returns false
6458          * @type String
6459          */
6460         'alphaText' : 'This field should only contain letters and _',
6461         /**
6462          * The keystroke filter mask to be applied on alpha input
6463          * @type RegExp
6464          */
6465         'alphaMask' : /[a-z_]/i,
6466
6467         /**
6468          * The function used to validate alphanumeric values
6469          * @param {String} value The value
6470          */
6471         'alphanum' : function(v){
6472             return alphanum.test(v);
6473         },
6474         /**
6475          * The error text to display when the alphanumeric validation function returns false
6476          * @type String
6477          */
6478         'alphanumText' : 'This field should only contain letters, numbers and _',
6479         /**
6480          * The keystroke filter mask to be applied on alphanumeric input
6481          * @type RegExp
6482          */
6483         'alphanumMask' : /[a-z0-9_]/i
6484     };
6485 }();/*
6486  * - LGPL
6487  *
6488  * Input
6489  * 
6490  */
6491
6492 /**
6493  * @class Roo.bootstrap.Input
6494  * @extends Roo.bootstrap.Component
6495  * Bootstrap Input class
6496  * @cfg {Boolean} disabled is it disabled
6497  * @cfg {String} fieldLabel - the label associated
6498  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6499  * @cfg {String} name name of the input
6500  * @cfg {string} fieldLabel - the label associated
6501  * @cfg {string}  inputType - input / file submit ...
6502  * @cfg {string} placeholder - placeholder to put in text.
6503  * @cfg {string}  before - input group add on before
6504  * @cfg {string} after - input group add on after
6505  * @cfg {string} size - (lg|sm) or leave empty..
6506  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6507  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6508  * @cfg {Number} md colspan out of 12 for computer-sized screens
6509  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6510  * @cfg {string} value default value of the input
6511  * @cfg {Number} labelWidth set the width of label (0-12)
6512  * @cfg {String} labelAlign (top|left)
6513  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6514  * @cfg {String} align (left|center|right) Default left
6515  * 
6516  * 
6517  * @constructor
6518  * Create a new Input
6519  * @param {Object} config The config object
6520  */
6521
6522 Roo.bootstrap.Input = function(config){
6523     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6524    
6525         this.addEvents({
6526             /**
6527              * @event focus
6528              * Fires when this field receives input focus.
6529              * @param {Roo.form.Field} this
6530              */
6531             focus : true,
6532             /**
6533              * @event blur
6534              * Fires when this field loses input focus.
6535              * @param {Roo.form.Field} this
6536              */
6537             blur : true,
6538             /**
6539              * @event specialkey
6540              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6541              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6542              * @param {Roo.form.Field} this
6543              * @param {Roo.EventObject} e The event object
6544              */
6545             specialkey : true,
6546             /**
6547              * @event change
6548              * Fires just before the field blurs if the field value has changed.
6549              * @param {Roo.form.Field} this
6550              * @param {Mixed} newValue The new value
6551              * @param {Mixed} oldValue The original value
6552              */
6553             change : true,
6554             /**
6555              * @event invalid
6556              * Fires after the field has been marked as invalid.
6557              * @param {Roo.form.Field} this
6558              * @param {String} msg The validation message
6559              */
6560             invalid : true,
6561             /**
6562              * @event valid
6563              * Fires after the field has been validated with no errors.
6564              * @param {Roo.form.Field} this
6565              */
6566             valid : true,
6567              /**
6568              * @event keyup
6569              * Fires after the key up
6570              * @param {Roo.form.Field} this
6571              * @param {Roo.EventObject}  e The event Object
6572              */
6573             keyup : true
6574         });
6575 };
6576
6577 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6578      /**
6579      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6580       automatic validation (defaults to "keyup").
6581      */
6582     validationEvent : "keyup",
6583      /**
6584      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6585      */
6586     validateOnBlur : true,
6587     /**
6588      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6589      */
6590     validationDelay : 250,
6591      /**
6592      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6593      */
6594     focusClass : "x-form-focus",  // not needed???
6595     
6596        
6597     /**
6598      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6599      */
6600     invalidClass : "has-error",
6601     
6602     /**
6603      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6604      */
6605     selectOnFocus : false,
6606     
6607      /**
6608      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6609      */
6610     maskRe : null,
6611        /**
6612      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6613      */
6614     vtype : null,
6615     
6616       /**
6617      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6618      */
6619     disableKeyFilter : false,
6620     
6621        /**
6622      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6623      */
6624     disabled : false,
6625      /**
6626      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6627      */
6628     allowBlank : true,
6629     /**
6630      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6631      */
6632     blankText : "This field is required",
6633     
6634      /**
6635      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6636      */
6637     minLength : 0,
6638     /**
6639      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6640      */
6641     maxLength : Number.MAX_VALUE,
6642     /**
6643      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6644      */
6645     minLengthText : "The minimum length for this field is {0}",
6646     /**
6647      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6648      */
6649     maxLengthText : "The maximum length for this field is {0}",
6650   
6651     
6652     /**
6653      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6654      * If available, this function will be called only after the basic validators all return true, and will be passed the
6655      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6656      */
6657     validator : null,
6658     /**
6659      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6660      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6661      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6662      */
6663     regex : null,
6664     /**
6665      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6666      */
6667     regexText : "",
6668     
6669     
6670     
6671     fieldLabel : '',
6672     inputType : 'text',
6673     
6674     name : false,
6675     placeholder: false,
6676     before : false,
6677     after : false,
6678     size : false,
6679     // private
6680     hasFocus : false,
6681     preventMark: false,
6682     isFormField : true,
6683     value : '',
6684     labelWidth : 2,
6685     labelAlign : false,
6686     readOnly : false,
6687     align : false,
6688     formatedValue : false,
6689     
6690     parentLabelAlign : function()
6691     {
6692         var parent = this;
6693         while (parent.parent()) {
6694             parent = parent.parent();
6695             if (typeof(parent.labelAlign) !='undefined') {
6696                 return parent.labelAlign;
6697             }
6698         }
6699         return 'left';
6700         
6701     },
6702     
6703     getAutoCreate : function(){
6704         
6705         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6706         
6707         var id = Roo.id();
6708         
6709         var cfg = {};
6710         
6711         if(this.inputType != 'hidden'){
6712             cfg.cls = 'form-group' //input-group
6713         }
6714         
6715         var input =  {
6716             tag: 'input',
6717             id : id,
6718             type : this.inputType,
6719             value : this.value,
6720             cls : 'form-control',
6721             placeholder : this.placeholder || ''
6722             
6723         };
6724         
6725         if(this.align){
6726             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6727         }
6728         
6729         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6730             input.maxLength = this.maxLength;
6731         }
6732         
6733         if (this.disabled) {
6734             input.disabled=true;
6735         }
6736         
6737         if (this.readOnly) {
6738             input.readonly=true;
6739         }
6740         
6741         if (this.name) {
6742             input.name = this.name;
6743         }
6744         if (this.size) {
6745             input.cls += ' input-' + this.size;
6746         }
6747         var settings=this;
6748         ['xs','sm','md','lg'].map(function(size){
6749             if (settings[size]) {
6750                 cfg.cls += ' col-' + size + '-' + settings[size];
6751             }
6752         });
6753         
6754         var inputblock = input;
6755         
6756         if (this.before || this.after) {
6757             
6758             inputblock = {
6759                 cls : 'input-group',
6760                 cn :  [] 
6761             };
6762             if (this.before && typeof(this.before) == 'string') {
6763                 
6764                 inputblock.cn.push({
6765                     tag :'span',
6766                     cls : 'roo-input-before input-group-addon',
6767                     html : this.before
6768                 });
6769             }
6770             if (this.before && typeof(this.before) == 'object') {
6771                 this.before = Roo.factory(this.before);
6772                 Roo.log(this.before);
6773                 inputblock.cn.push({
6774                     tag :'span',
6775                     cls : 'roo-input-before input-group-' +
6776                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6777                 });
6778             }
6779             
6780             inputblock.cn.push(input);
6781             
6782             if (this.after && typeof(this.after) == 'string') {
6783                 inputblock.cn.push({
6784                     tag :'span',
6785                     cls : 'roo-input-after input-group-addon',
6786                     html : this.after
6787                 });
6788             }
6789             if (this.after && typeof(this.after) == 'object') {
6790                 this.after = Roo.factory(this.after);
6791                 Roo.log(this.after);
6792                 inputblock.cn.push({
6793                     tag :'span',
6794                     cls : 'roo-input-after input-group-' +
6795                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6796                 });
6797             }
6798         };
6799         
6800         if (align ==='left' && this.fieldLabel.length) {
6801                 Roo.log("left and has label");
6802                 cfg.cn = [
6803                     
6804                     {
6805                         tag: 'label',
6806                         'for' :  id,
6807                         cls : 'control-label col-sm-' + this.labelWidth,
6808                         html : this.fieldLabel
6809                         
6810                     },
6811                     {
6812                         cls : "col-sm-" + (12 - this.labelWidth), 
6813                         cn: [
6814                             inputblock
6815                         ]
6816                     }
6817                     
6818                 ];
6819         } else if ( this.fieldLabel.length) {
6820                 Roo.log(" label");
6821                  cfg.cn = [
6822                    
6823                     {
6824                         tag: 'label',
6825                         //cls : 'input-group-addon',
6826                         html : this.fieldLabel
6827                         
6828                     },
6829                     
6830                     inputblock
6831                     
6832                 ];
6833
6834         } else {
6835             
6836                 Roo.log(" no label && no align");
6837                 cfg.cn = [
6838                     
6839                         inputblock
6840                     
6841                 ];
6842                 
6843                 
6844         };
6845         Roo.log('input-parentType: ' + this.parentType);
6846         
6847         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6848            cfg.cls += ' navbar-form';
6849            Roo.log(cfg);
6850         }
6851         
6852         return cfg;
6853         
6854     },
6855     /**
6856      * return the real input element.
6857      */
6858     inputEl: function ()
6859     {
6860         return this.el.select('input.form-control',true).first();
6861     },
6862     setDisabled : function(v)
6863     {
6864         var i  = this.inputEl().dom;
6865         if (!v) {
6866             i.removeAttribute('disabled');
6867             return;
6868             
6869         }
6870         i.setAttribute('disabled','true');
6871     },
6872     initEvents : function()
6873     {
6874         
6875         this.inputEl().on("keydown" , this.fireKey,  this);
6876         this.inputEl().on("focus", this.onFocus,  this);
6877         this.inputEl().on("blur", this.onBlur,  this);
6878         
6879         this.inputEl().relayEvent('keyup', this);
6880
6881         // reference to original value for reset
6882         this.originalValue = this.getValue();
6883         //Roo.form.TextField.superclass.initEvents.call(this);
6884         if(this.validationEvent == 'keyup'){
6885             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
6886             this.inputEl().on('keyup', this.filterValidation, this);
6887         }
6888         else if(this.validationEvent !== false){
6889             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
6890         }
6891         
6892         if(this.selectOnFocus){
6893             this.on("focus", this.preFocus, this);
6894             
6895         }
6896         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
6897             this.inputEl().on("keypress", this.filterKeys, this);
6898         }
6899        /* if(this.grow){
6900             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
6901             this.el.on("click", this.autoSize,  this);
6902         }
6903         */
6904         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
6905             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
6906         }
6907         
6908         if (typeof(this.before) == 'object') {
6909             this.before.render(this.el.select('.roo-input-before',true).first());
6910         }
6911         if (typeof(this.after) == 'object') {
6912             this.after.render(this.el.select('.roo-input-after',true).first());
6913         }
6914         
6915         
6916     },
6917     filterValidation : function(e){
6918         if(!e.isNavKeyPress()){
6919             this.validationTask.delay(this.validationDelay);
6920         }
6921     },
6922      /**
6923      * Validates the field value
6924      * @return {Boolean} True if the value is valid, else false
6925      */
6926     validate : function(){
6927         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
6928         if(this.disabled || this.validateValue(this.getRawValue())){
6929             this.clearInvalid();
6930             return true;
6931         }
6932         return false;
6933     },
6934     
6935     
6936     /**
6937      * Validates a value according to the field's validation rules and marks the field as invalid
6938      * if the validation fails
6939      * @param {Mixed} value The value to validate
6940      * @return {Boolean} True if the value is valid, else false
6941      */
6942     validateValue : function(value){
6943         if(value.length < 1)  { // if it's blank
6944              if(this.allowBlank){
6945                 this.clearInvalid();
6946                 return true;
6947              }else{
6948                 this.markInvalid(this.blankText);
6949                 return false;
6950              }
6951         }
6952         if(value.length < this.minLength){
6953             this.markInvalid(String.format(this.minLengthText, this.minLength));
6954             return false;
6955         }
6956         if(value.length > this.maxLength){
6957             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
6958             return false;
6959         }
6960         if(this.vtype){
6961             var vt = Roo.form.VTypes;
6962             if(!vt[this.vtype](value, this)){
6963                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
6964                 return false;
6965             }
6966         }
6967         if(typeof this.validator == "function"){
6968             var msg = this.validator(value);
6969             if(msg !== true){
6970                 this.markInvalid(msg);
6971                 return false;
6972             }
6973         }
6974         if(this.regex && !this.regex.test(value)){
6975             this.markInvalid(this.regexText);
6976             return false;
6977         }
6978         return true;
6979     },
6980
6981     
6982     
6983      // private
6984     fireKey : function(e){
6985         //Roo.log('field ' + e.getKey());
6986         if(e.isNavKeyPress()){
6987             this.fireEvent("specialkey", this, e);
6988         }
6989     },
6990     focus : function (selectText){
6991         if(this.rendered){
6992             this.inputEl().focus();
6993             if(selectText === true){
6994                 this.inputEl().dom.select();
6995             }
6996         }
6997         return this;
6998     } ,
6999     
7000     onFocus : function(){
7001         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7002            // this.el.addClass(this.focusClass);
7003         }
7004         if(!this.hasFocus){
7005             this.hasFocus = true;
7006             this.startValue = this.getValue();
7007             this.fireEvent("focus", this);
7008         }
7009     },
7010     
7011     beforeBlur : Roo.emptyFn,
7012
7013     
7014     // private
7015     onBlur : function(){
7016         this.beforeBlur();
7017         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7018             //this.el.removeClass(this.focusClass);
7019         }
7020         this.hasFocus = false;
7021         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7022             this.validate();
7023         }
7024         var v = this.getValue();
7025         if(String(v) !== String(this.startValue)){
7026             this.fireEvent('change', this, v, this.startValue);
7027         }
7028         this.fireEvent("blur", this);
7029     },
7030     
7031     /**
7032      * Resets the current field value to the originally loaded value and clears any validation messages
7033      */
7034     reset : function(){
7035         this.setValue(this.originalValue);
7036         this.clearInvalid();
7037     },
7038      /**
7039      * Returns the name of the field
7040      * @return {Mixed} name The name field
7041      */
7042     getName: function(){
7043         return this.name;
7044     },
7045      /**
7046      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7047      * @return {Mixed} value The field value
7048      */
7049     getValue : function(){
7050         
7051         var v = this.inputEl().getValue();
7052         
7053         return v;
7054     },
7055     /**
7056      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7057      * @return {Mixed} value The field value
7058      */
7059     getRawValue : function(){
7060         var v = this.inputEl().getValue();
7061         
7062         return v;
7063     },
7064     
7065     /**
7066      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7067      * @param {Mixed} value The value to set
7068      */
7069     setRawValue : function(v){
7070         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7071     },
7072     
7073     selectText : function(start, end){
7074         var v = this.getRawValue();
7075         if(v.length > 0){
7076             start = start === undefined ? 0 : start;
7077             end = end === undefined ? v.length : end;
7078             var d = this.inputEl().dom;
7079             if(d.setSelectionRange){
7080                 d.setSelectionRange(start, end);
7081             }else if(d.createTextRange){
7082                 var range = d.createTextRange();
7083                 range.moveStart("character", start);
7084                 range.moveEnd("character", v.length-end);
7085                 range.select();
7086             }
7087         }
7088     },
7089     
7090     /**
7091      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7092      * @param {Mixed} value The value to set
7093      */
7094     setValue : function(v){
7095         this.value = v;
7096         if(this.rendered){
7097             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7098             this.validate();
7099         }
7100     },
7101     
7102     /*
7103     processValue : function(value){
7104         if(this.stripCharsRe){
7105             var newValue = value.replace(this.stripCharsRe, '');
7106             if(newValue !== value){
7107                 this.setRawValue(newValue);
7108                 return newValue;
7109             }
7110         }
7111         return value;
7112     },
7113   */
7114     preFocus : function(){
7115         
7116         if(this.selectOnFocus){
7117             this.inputEl().dom.select();
7118         }
7119     },
7120     filterKeys : function(e){
7121         var k = e.getKey();
7122         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7123             return;
7124         }
7125         var c = e.getCharCode(), cc = String.fromCharCode(c);
7126         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7127             return;
7128         }
7129         if(!this.maskRe.test(cc)){
7130             e.stopEvent();
7131         }
7132     },
7133      /**
7134      * Clear any invalid styles/messages for this field
7135      */
7136     clearInvalid : function(){
7137         
7138         if(!this.el || this.preventMark){ // not rendered
7139             return;
7140         }
7141         this.el.removeClass(this.invalidClass);
7142         /*
7143         switch(this.msgTarget){
7144             case 'qtip':
7145                 this.el.dom.qtip = '';
7146                 break;
7147             case 'title':
7148                 this.el.dom.title = '';
7149                 break;
7150             case 'under':
7151                 if(this.errorEl){
7152                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7153                 }
7154                 break;
7155             case 'side':
7156                 if(this.errorIcon){
7157                     this.errorIcon.dom.qtip = '';
7158                     this.errorIcon.hide();
7159                     this.un('resize', this.alignErrorIcon, this);
7160                 }
7161                 break;
7162             default:
7163                 var t = Roo.getDom(this.msgTarget);
7164                 t.innerHTML = '';
7165                 t.style.display = 'none';
7166                 break;
7167         }
7168         */
7169         this.fireEvent('valid', this);
7170     },
7171      /**
7172      * Mark this field as invalid
7173      * @param {String} msg The validation message
7174      */
7175     markInvalid : function(msg){
7176         if(!this.el  || this.preventMark){ // not rendered
7177             return;
7178         }
7179         this.el.addClass(this.invalidClass);
7180         /*
7181         msg = msg || this.invalidText;
7182         switch(this.msgTarget){
7183             case 'qtip':
7184                 this.el.dom.qtip = msg;
7185                 this.el.dom.qclass = 'x-form-invalid-tip';
7186                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7187                     Roo.QuickTips.enable();
7188                 }
7189                 break;
7190             case 'title':
7191                 this.el.dom.title = msg;
7192                 break;
7193             case 'under':
7194                 if(!this.errorEl){
7195                     var elp = this.el.findParent('.x-form-element', 5, true);
7196                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7197                     this.errorEl.setWidth(elp.getWidth(true)-20);
7198                 }
7199                 this.errorEl.update(msg);
7200                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7201                 break;
7202             case 'side':
7203                 if(!this.errorIcon){
7204                     var elp = this.el.findParent('.x-form-element', 5, true);
7205                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7206                 }
7207                 this.alignErrorIcon();
7208                 this.errorIcon.dom.qtip = msg;
7209                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7210                 this.errorIcon.show();
7211                 this.on('resize', this.alignErrorIcon, this);
7212                 break;
7213             default:
7214                 var t = Roo.getDom(this.msgTarget);
7215                 t.innerHTML = msg;
7216                 t.style.display = this.msgDisplay;
7217                 break;
7218         }
7219         */
7220         this.fireEvent('invalid', this, msg);
7221     },
7222     // private
7223     SafariOnKeyDown : function(event)
7224     {
7225         // this is a workaround for a password hang bug on chrome/ webkit.
7226         
7227         var isSelectAll = false;
7228         
7229         if(this.inputEl().dom.selectionEnd > 0){
7230             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7231         }
7232         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7233             event.preventDefault();
7234             this.setValue('');
7235             return;
7236         }
7237         
7238         if(isSelectAll){ // backspace and delete key
7239             
7240             event.preventDefault();
7241             // this is very hacky as keydown always get's upper case.
7242             //
7243             var cc = String.fromCharCode(event.getCharCode());
7244             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7245             
7246         }
7247     },
7248     adjustWidth : function(tag, w){
7249         tag = tag.toLowerCase();
7250         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7251             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7252                 if(tag == 'input'){
7253                     return w + 2;
7254                 }
7255                 if(tag == 'textarea'){
7256                     return w-2;
7257                 }
7258             }else if(Roo.isOpera){
7259                 if(tag == 'input'){
7260                     return w + 2;
7261                 }
7262                 if(tag == 'textarea'){
7263                     return w-2;
7264                 }
7265             }
7266         }
7267         return w;
7268     }
7269     
7270 });
7271
7272  
7273 /*
7274  * - LGPL
7275  *
7276  * Input
7277  * 
7278  */
7279
7280 /**
7281  * @class Roo.bootstrap.TextArea
7282  * @extends Roo.bootstrap.Input
7283  * Bootstrap TextArea class
7284  * @cfg {Number} cols Specifies the visible width of a text area
7285  * @cfg {Number} rows Specifies the visible number of lines in a text area
7286  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7287  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7288  * @cfg {string} html text
7289  * 
7290  * @constructor
7291  * Create a new TextArea
7292  * @param {Object} config The config object
7293  */
7294
7295 Roo.bootstrap.TextArea = function(config){
7296     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7297    
7298 };
7299
7300 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7301      
7302     cols : false,
7303     rows : 5,
7304     readOnly : false,
7305     warp : 'soft',
7306     resize : false,
7307     value: false,
7308     html: false,
7309     
7310     getAutoCreate : function(){
7311         
7312         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7313         
7314         var id = Roo.id();
7315         
7316         var cfg = {};
7317         
7318         var input =  {
7319             tag: 'textarea',
7320             id : id,
7321             warp : this.warp,
7322             rows : this.rows,
7323             value : this.value || '',
7324             html: this.html || '',
7325             cls : 'form-control',
7326             placeholder : this.placeholder || '' 
7327             
7328         };
7329         
7330         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7331             input.maxLength = this.maxLength;
7332         }
7333         
7334         if(this.resize){
7335             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7336         }
7337         
7338         if(this.cols){
7339             input.cols = this.cols;
7340         }
7341         
7342         if (this.readOnly) {
7343             input.readonly = true;
7344         }
7345         
7346         if (this.name) {
7347             input.name = this.name;
7348         }
7349         
7350         if (this.size) {
7351             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7352         }
7353         
7354         var settings=this;
7355         ['xs','sm','md','lg'].map(function(size){
7356             if (settings[size]) {
7357                 cfg.cls += ' col-' + size + '-' + settings[size];
7358             }
7359         });
7360         
7361         var inputblock = input;
7362         
7363         if (this.before || this.after) {
7364             
7365             inputblock = {
7366                 cls : 'input-group',
7367                 cn :  [] 
7368             };
7369             if (this.before) {
7370                 inputblock.cn.push({
7371                     tag :'span',
7372                     cls : 'input-group-addon',
7373                     html : this.before
7374                 });
7375             }
7376             inputblock.cn.push(input);
7377             if (this.after) {
7378                 inputblock.cn.push({
7379                     tag :'span',
7380                     cls : 'input-group-addon',
7381                     html : this.after
7382                 });
7383             }
7384             
7385         }
7386         
7387         if (align ==='left' && this.fieldLabel.length) {
7388                 Roo.log("left and has label");
7389                 cfg.cn = [
7390                     
7391                     {
7392                         tag: 'label',
7393                         'for' :  id,
7394                         cls : 'control-label col-sm-' + this.labelWidth,
7395                         html : this.fieldLabel
7396                         
7397                     },
7398                     {
7399                         cls : "col-sm-" + (12 - this.labelWidth), 
7400                         cn: [
7401                             inputblock
7402                         ]
7403                     }
7404                     
7405                 ];
7406         } else if ( this.fieldLabel.length) {
7407                 Roo.log(" label");
7408                  cfg.cn = [
7409                    
7410                     {
7411                         tag: 'label',
7412                         //cls : 'input-group-addon',
7413                         html : this.fieldLabel
7414                         
7415                     },
7416                     
7417                     inputblock
7418                     
7419                 ];
7420
7421         } else {
7422             
7423                    Roo.log(" no label && no align");
7424                 cfg.cn = [
7425                     
7426                         inputblock
7427                     
7428                 ];
7429                 
7430                 
7431         }
7432         
7433         if (this.disabled) {
7434             input.disabled=true;
7435         }
7436         
7437         return cfg;
7438         
7439     },
7440     /**
7441      * return the real textarea element.
7442      */
7443     inputEl: function ()
7444     {
7445         return this.el.select('textarea.form-control',true).first();
7446     }
7447 });
7448
7449  
7450 /*
7451  * - LGPL
7452  *
7453  * trigger field - base class for combo..
7454  * 
7455  */
7456  
7457 /**
7458  * @class Roo.bootstrap.TriggerField
7459  * @extends Roo.bootstrap.Input
7460  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7461  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7462  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7463  * for which you can provide a custom implementation.  For example:
7464  * <pre><code>
7465 var trigger = new Roo.bootstrap.TriggerField();
7466 trigger.onTriggerClick = myTriggerFn;
7467 trigger.applyTo('my-field');
7468 </code></pre>
7469  *
7470  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7471  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7472  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7473  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7474  * @constructor
7475  * Create a new TriggerField.
7476  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7477  * to the base TextField)
7478  */
7479 Roo.bootstrap.TriggerField = function(config){
7480     this.mimicing = false;
7481     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7482 };
7483
7484 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7485     /**
7486      * @cfg {String} triggerClass A CSS class to apply to the trigger
7487      */
7488      /**
7489      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7490      */
7491     hideTrigger:false,
7492
7493     /** @cfg {Boolean} grow @hide */
7494     /** @cfg {Number} growMin @hide */
7495     /** @cfg {Number} growMax @hide */
7496
7497     /**
7498      * @hide 
7499      * @method
7500      */
7501     autoSize: Roo.emptyFn,
7502     // private
7503     monitorTab : true,
7504     // private
7505     deferHeight : true,
7506
7507     
7508     actionMode : 'wrap',
7509     
7510     
7511     
7512     getAutoCreate : function(){
7513        
7514         var parent = this.parent();
7515         
7516         var align = this.labelAlign || this.parentLabelAlign();
7517         
7518         var id = Roo.id();
7519         
7520         var cfg = {
7521             cls: 'form-group' //input-group
7522         };
7523         
7524         
7525         var input =  {
7526             tag: 'input',
7527             id : id,
7528             type : this.inputType,
7529             cls : 'form-control',
7530             autocomplete: 'off',
7531             placeholder : this.placeholder || '' 
7532             
7533         };
7534         if (this.name) {
7535             input.name = this.name;
7536         }
7537         if (this.size) {
7538             input.cls += ' input-' + this.size;
7539         }
7540         
7541         if (this.disabled) {
7542             input.disabled=true;
7543         }
7544         
7545         var inputblock = input;
7546         
7547         if (this.before || this.after) {
7548             
7549             inputblock = {
7550                 cls : 'input-group',
7551                 cn :  [] 
7552             };
7553             if (this.before) {
7554                 inputblock.cn.push({
7555                     tag :'span',
7556                     cls : 'input-group-addon',
7557                     html : this.before
7558                 });
7559             }
7560             inputblock.cn.push(input);
7561             if (this.after) {
7562                 inputblock.cn.push({
7563                     tag :'span',
7564                     cls : 'input-group-addon',
7565                     html : this.after
7566                 });
7567             }
7568             
7569         };
7570         
7571         var box = {
7572             tag: 'div',
7573             cn: [
7574                 {
7575                     tag: 'input',
7576                     type : 'hidden',
7577                     cls: 'form-hidden-field'
7578                 },
7579                 inputblock
7580             ]
7581             
7582         };
7583         
7584         if(this.multiple){
7585             Roo.log('multiple');
7586             
7587             box = {
7588                 tag: 'div',
7589                 cn: [
7590                     {
7591                         tag: 'input',
7592                         type : 'hidden',
7593                         cls: 'form-hidden-field'
7594                     },
7595                     {
7596                         tag: 'ul',
7597                         cls: 'select2-choices',
7598                         cn:[
7599                             {
7600                                 tag: 'li',
7601                                 cls: 'select2-search-field',
7602                                 cn: [
7603
7604                                     inputblock
7605                                 ]
7606                             }
7607                         ]
7608                     }
7609                 ]
7610             }
7611         };
7612         
7613         var combobox = {
7614             cls: 'select2-container input-group',
7615             cn: [
7616                 box,
7617                 {
7618                     tag: 'ul',
7619                     cls: 'typeahead typeahead-long dropdown-menu',
7620                     style: 'display:none'
7621                 }
7622             ]
7623         };
7624         
7625         if(!this.multiple){
7626             combobox.cn.push({
7627                 tag :'span',
7628                 cls : 'input-group-addon btn dropdown-toggle',
7629                 cn : [
7630                     {
7631                         tag: 'span',
7632                         cls: 'caret'
7633                     },
7634                     {
7635                         tag: 'span',
7636                         cls: 'combobox-clear',
7637                         cn  : [
7638                             {
7639                                 tag : 'i',
7640                                 cls: 'icon-remove'
7641                             }
7642                         ]
7643                     }
7644                 ]
7645
7646             })
7647         }
7648         
7649         if(this.multiple){
7650             combobox.cls += ' select2-container-multi';
7651         }
7652         
7653         if (align ==='left' && this.fieldLabel.length) {
7654             
7655                 Roo.log("left and has label");
7656                 cfg.cn = [
7657                     
7658                     {
7659                         tag: 'label',
7660                         'for' :  id,
7661                         cls : 'control-label col-sm-' + this.labelWidth,
7662                         html : this.fieldLabel
7663                         
7664                     },
7665                     {
7666                         cls : "col-sm-" + (12 - this.labelWidth), 
7667                         cn: [
7668                             combobox
7669                         ]
7670                     }
7671                     
7672                 ];
7673         } else if ( this.fieldLabel.length) {
7674                 Roo.log(" label");
7675                  cfg.cn = [
7676                    
7677                     {
7678                         tag: 'label',
7679                         //cls : 'input-group-addon',
7680                         html : this.fieldLabel
7681                         
7682                     },
7683                     
7684                     combobox
7685                     
7686                 ];
7687
7688         } else {
7689             
7690                 Roo.log(" no label && no align");
7691                 cfg = combobox
7692                      
7693                 
7694         }
7695          
7696         var settings=this;
7697         ['xs','sm','md','lg'].map(function(size){
7698             if (settings[size]) {
7699                 cfg.cls += ' col-' + size + '-' + settings[size];
7700             }
7701         });
7702         
7703         return cfg;
7704         
7705     },
7706     
7707     
7708     
7709     // private
7710     onResize : function(w, h){
7711 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7712 //        if(typeof w == 'number'){
7713 //            var x = w - this.trigger.getWidth();
7714 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7715 //            this.trigger.setStyle('left', x+'px');
7716 //        }
7717     },
7718
7719     // private
7720     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7721
7722     // private
7723     getResizeEl : function(){
7724         return this.inputEl();
7725     },
7726
7727     // private
7728     getPositionEl : function(){
7729         return this.inputEl();
7730     },
7731
7732     // private
7733     alignErrorIcon : function(){
7734         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7735     },
7736
7737     // private
7738     initEvents : function(){
7739         
7740         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7741         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7742         if(!this.multiple){
7743             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7744             if(this.hideTrigger){
7745                 this.trigger.setDisplayed(false);
7746             }
7747             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7748         }
7749         
7750         if(this.multiple){
7751             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7752         }
7753         
7754         //this.trigger.addClassOnOver('x-form-trigger-over');
7755         //this.trigger.addClassOnClick('x-form-trigger-click');
7756         
7757         //if(!this.width){
7758         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7759         //}
7760     },
7761
7762     // private
7763     initTrigger : function(){
7764        
7765     },
7766
7767     // private
7768     onDestroy : function(){
7769         if(this.trigger){
7770             this.trigger.removeAllListeners();
7771           //  this.trigger.remove();
7772         }
7773         //if(this.wrap){
7774         //    this.wrap.remove();
7775         //}
7776         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
7777     },
7778
7779     // private
7780     onFocus : function(){
7781         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
7782         /*
7783         if(!this.mimicing){
7784             this.wrap.addClass('x-trigger-wrap-focus');
7785             this.mimicing = true;
7786             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
7787             if(this.monitorTab){
7788                 this.el.on("keydown", this.checkTab, this);
7789             }
7790         }
7791         */
7792     },
7793
7794     // private
7795     checkTab : function(e){
7796         if(e.getKey() == e.TAB){
7797             this.triggerBlur();
7798         }
7799     },
7800
7801     // private
7802     onBlur : function(){
7803         // do nothing
7804     },
7805
7806     // private
7807     mimicBlur : function(e, t){
7808         /*
7809         if(!this.wrap.contains(t) && this.validateBlur()){
7810             this.triggerBlur();
7811         }
7812         */
7813     },
7814
7815     // private
7816     triggerBlur : function(){
7817         this.mimicing = false;
7818         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7819         if(this.monitorTab){
7820             this.el.un("keydown", this.checkTab, this);
7821         }
7822         //this.wrap.removeClass('x-trigger-wrap-focus');
7823         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7824     },
7825
7826     // private
7827     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7828     validateBlur : function(e, t){
7829         return true;
7830     },
7831
7832     // private
7833     onDisable : function(){
7834         this.inputEl().dom.disabled = true;
7835         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7836         //if(this.wrap){
7837         //    this.wrap.addClass('x-item-disabled');
7838         //}
7839     },
7840
7841     // private
7842     onEnable : function(){
7843         this.inputEl().dom.disabled = false;
7844         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
7845         //if(this.wrap){
7846         //    this.el.removeClass('x-item-disabled');
7847         //}
7848     },
7849
7850     // private
7851     onShow : function(){
7852         var ae = this.getActionEl();
7853         
7854         if(ae){
7855             ae.dom.style.display = '';
7856             ae.dom.style.visibility = 'visible';
7857         }
7858     },
7859
7860     // private
7861     
7862     onHide : function(){
7863         var ae = this.getActionEl();
7864         ae.dom.style.display = 'none';
7865     },
7866
7867     /**
7868      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
7869      * by an implementing function.
7870      * @method
7871      * @param {EventObject} e
7872      */
7873     onTriggerClick : Roo.emptyFn
7874 });
7875  /*
7876  * Based on:
7877  * Ext JS Library 1.1.1
7878  * Copyright(c) 2006-2007, Ext JS, LLC.
7879  *
7880  * Originally Released Under LGPL - original licence link has changed is not relivant.
7881  *
7882  * Fork - LGPL
7883  * <script type="text/javascript">
7884  */
7885
7886
7887 /**
7888  * @class Roo.data.SortTypes
7889  * @singleton
7890  * Defines the default sorting (casting?) comparison functions used when sorting data.
7891  */
7892 Roo.data.SortTypes = {
7893     /**
7894      * Default sort that does nothing
7895      * @param {Mixed} s The value being converted
7896      * @return {Mixed} The comparison value
7897      */
7898     none : function(s){
7899         return s;
7900     },
7901     
7902     /**
7903      * The regular expression used to strip tags
7904      * @type {RegExp}
7905      * @property
7906      */
7907     stripTagsRE : /<\/?[^>]+>/gi,
7908     
7909     /**
7910      * Strips all HTML tags to sort on text only
7911      * @param {Mixed} s The value being converted
7912      * @return {String} The comparison value
7913      */
7914     asText : function(s){
7915         return String(s).replace(this.stripTagsRE, "");
7916     },
7917     
7918     /**
7919      * Strips all HTML tags to sort on text only - Case insensitive
7920      * @param {Mixed} s The value being converted
7921      * @return {String} The comparison value
7922      */
7923     asUCText : function(s){
7924         return String(s).toUpperCase().replace(this.stripTagsRE, "");
7925     },
7926     
7927     /**
7928      * Case insensitive string
7929      * @param {Mixed} s The value being converted
7930      * @return {String} The comparison value
7931      */
7932     asUCString : function(s) {
7933         return String(s).toUpperCase();
7934     },
7935     
7936     /**
7937      * Date sorting
7938      * @param {Mixed} s The value being converted
7939      * @return {Number} The comparison value
7940      */
7941     asDate : function(s) {
7942         if(!s){
7943             return 0;
7944         }
7945         if(s instanceof Date){
7946             return s.getTime();
7947         }
7948         return Date.parse(String(s));
7949     },
7950     
7951     /**
7952      * Float sorting
7953      * @param {Mixed} s The value being converted
7954      * @return {Float} The comparison value
7955      */
7956     asFloat : function(s) {
7957         var val = parseFloat(String(s).replace(/,/g, ""));
7958         if(isNaN(val)) val = 0;
7959         return val;
7960     },
7961     
7962     /**
7963      * Integer sorting
7964      * @param {Mixed} s The value being converted
7965      * @return {Number} The comparison value
7966      */
7967     asInt : function(s) {
7968         var val = parseInt(String(s).replace(/,/g, ""));
7969         if(isNaN(val)) val = 0;
7970         return val;
7971     }
7972 };/*
7973  * Based on:
7974  * Ext JS Library 1.1.1
7975  * Copyright(c) 2006-2007, Ext JS, LLC.
7976  *
7977  * Originally Released Under LGPL - original licence link has changed is not relivant.
7978  *
7979  * Fork - LGPL
7980  * <script type="text/javascript">
7981  */
7982
7983 /**
7984 * @class Roo.data.Record
7985  * Instances of this class encapsulate both record <em>definition</em> information, and record
7986  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
7987  * to access Records cached in an {@link Roo.data.Store} object.<br>
7988  * <p>
7989  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
7990  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
7991  * objects.<br>
7992  * <p>
7993  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
7994  * @constructor
7995  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
7996  * {@link #create}. The parameters are the same.
7997  * @param {Array} data An associative Array of data values keyed by the field name.
7998  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
7999  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8000  * not specified an integer id is generated.
8001  */
8002 Roo.data.Record = function(data, id){
8003     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8004     this.data = data;
8005 };
8006
8007 /**
8008  * Generate a constructor for a specific record layout.
8009  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8010  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8011  * Each field definition object may contain the following properties: <ul>
8012  * <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,
8013  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8014  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8015  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8016  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8017  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8018  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8019  * this may be omitted.</p></li>
8020  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8021  * <ul><li>auto (Default, implies no conversion)</li>
8022  * <li>string</li>
8023  * <li>int</li>
8024  * <li>float</li>
8025  * <li>boolean</li>
8026  * <li>date</li></ul></p></li>
8027  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8028  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8029  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8030  * by the Reader into an object that will be stored in the Record. It is passed the
8031  * following parameters:<ul>
8032  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8033  * </ul></p></li>
8034  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8035  * </ul>
8036  * <br>usage:<br><pre><code>
8037 var TopicRecord = Roo.data.Record.create(
8038     {name: 'title', mapping: 'topic_title'},
8039     {name: 'author', mapping: 'username'},
8040     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8041     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8042     {name: 'lastPoster', mapping: 'user2'},
8043     {name: 'excerpt', mapping: 'post_text'}
8044 );
8045
8046 var myNewRecord = new TopicRecord({
8047     title: 'Do my job please',
8048     author: 'noobie',
8049     totalPosts: 1,
8050     lastPost: new Date(),
8051     lastPoster: 'Animal',
8052     excerpt: 'No way dude!'
8053 });
8054 myStore.add(myNewRecord);
8055 </code></pre>
8056  * @method create
8057  * @static
8058  */
8059 Roo.data.Record.create = function(o){
8060     var f = function(){
8061         f.superclass.constructor.apply(this, arguments);
8062     };
8063     Roo.extend(f, Roo.data.Record);
8064     var p = f.prototype;
8065     p.fields = new Roo.util.MixedCollection(false, function(field){
8066         return field.name;
8067     });
8068     for(var i = 0, len = o.length; i < len; i++){
8069         p.fields.add(new Roo.data.Field(o[i]));
8070     }
8071     f.getField = function(name){
8072         return p.fields.get(name);  
8073     };
8074     return f;
8075 };
8076
8077 Roo.data.Record.AUTO_ID = 1000;
8078 Roo.data.Record.EDIT = 'edit';
8079 Roo.data.Record.REJECT = 'reject';
8080 Roo.data.Record.COMMIT = 'commit';
8081
8082 Roo.data.Record.prototype = {
8083     /**
8084      * Readonly flag - true if this record has been modified.
8085      * @type Boolean
8086      */
8087     dirty : false,
8088     editing : false,
8089     error: null,
8090     modified: null,
8091
8092     // private
8093     join : function(store){
8094         this.store = store;
8095     },
8096
8097     /**
8098      * Set the named field to the specified value.
8099      * @param {String} name The name of the field to set.
8100      * @param {Object} value The value to set the field to.
8101      */
8102     set : function(name, value){
8103         if(this.data[name] == value){
8104             return;
8105         }
8106         this.dirty = true;
8107         if(!this.modified){
8108             this.modified = {};
8109         }
8110         if(typeof this.modified[name] == 'undefined'){
8111             this.modified[name] = this.data[name];
8112         }
8113         this.data[name] = value;
8114         if(!this.editing && this.store){
8115             this.store.afterEdit(this);
8116         }       
8117     },
8118
8119     /**
8120      * Get the value of the named field.
8121      * @param {String} name The name of the field to get the value of.
8122      * @return {Object} The value of the field.
8123      */
8124     get : function(name){
8125         return this.data[name]; 
8126     },
8127
8128     // private
8129     beginEdit : function(){
8130         this.editing = true;
8131         this.modified = {}; 
8132     },
8133
8134     // private
8135     cancelEdit : function(){
8136         this.editing = false;
8137         delete this.modified;
8138     },
8139
8140     // private
8141     endEdit : function(){
8142         this.editing = false;
8143         if(this.dirty && this.store){
8144             this.store.afterEdit(this);
8145         }
8146     },
8147
8148     /**
8149      * Usually called by the {@link Roo.data.Store} which owns the Record.
8150      * Rejects all changes made to the Record since either creation, or the last commit operation.
8151      * Modified fields are reverted to their original values.
8152      * <p>
8153      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8154      * of reject operations.
8155      */
8156     reject : function(){
8157         var m = this.modified;
8158         for(var n in m){
8159             if(typeof m[n] != "function"){
8160                 this.data[n] = m[n];
8161             }
8162         }
8163         this.dirty = false;
8164         delete this.modified;
8165         this.editing = false;
8166         if(this.store){
8167             this.store.afterReject(this);
8168         }
8169     },
8170
8171     /**
8172      * Usually called by the {@link Roo.data.Store} which owns the Record.
8173      * Commits all changes made to the Record since either creation, or the last commit operation.
8174      * <p>
8175      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8176      * of commit operations.
8177      */
8178     commit : function(){
8179         this.dirty = false;
8180         delete this.modified;
8181         this.editing = false;
8182         if(this.store){
8183             this.store.afterCommit(this);
8184         }
8185     },
8186
8187     // private
8188     hasError : function(){
8189         return this.error != null;
8190     },
8191
8192     // private
8193     clearError : function(){
8194         this.error = null;
8195     },
8196
8197     /**
8198      * Creates a copy of this record.
8199      * @param {String} id (optional) A new record id if you don't want to use this record's id
8200      * @return {Record}
8201      */
8202     copy : function(newId) {
8203         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8204     }
8205 };/*
8206  * Based on:
8207  * Ext JS Library 1.1.1
8208  * Copyright(c) 2006-2007, Ext JS, LLC.
8209  *
8210  * Originally Released Under LGPL - original licence link has changed is not relivant.
8211  *
8212  * Fork - LGPL
8213  * <script type="text/javascript">
8214  */
8215
8216
8217
8218 /**
8219  * @class Roo.data.Store
8220  * @extends Roo.util.Observable
8221  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8222  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8223  * <p>
8224  * 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
8225  * has no knowledge of the format of the data returned by the Proxy.<br>
8226  * <p>
8227  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8228  * instances from the data object. These records are cached and made available through accessor functions.
8229  * @constructor
8230  * Creates a new Store.
8231  * @param {Object} config A config object containing the objects needed for the Store to access data,
8232  * and read the data into Records.
8233  */
8234 Roo.data.Store = function(config){
8235     this.data = new Roo.util.MixedCollection(false);
8236     this.data.getKey = function(o){
8237         return o.id;
8238     };
8239     this.baseParams = {};
8240     // private
8241     this.paramNames = {
8242         "start" : "start",
8243         "limit" : "limit",
8244         "sort" : "sort",
8245         "dir" : "dir",
8246         "multisort" : "_multisort"
8247     };
8248
8249     if(config && config.data){
8250         this.inlineData = config.data;
8251         delete config.data;
8252     }
8253
8254     Roo.apply(this, config);
8255     
8256     if(this.reader){ // reader passed
8257         this.reader = Roo.factory(this.reader, Roo.data);
8258         this.reader.xmodule = this.xmodule || false;
8259         if(!this.recordType){
8260             this.recordType = this.reader.recordType;
8261         }
8262         if(this.reader.onMetaChange){
8263             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8264         }
8265     }
8266
8267     if(this.recordType){
8268         this.fields = this.recordType.prototype.fields;
8269     }
8270     this.modified = [];
8271
8272     this.addEvents({
8273         /**
8274          * @event datachanged
8275          * Fires when the data cache has changed, and a widget which is using this Store
8276          * as a Record cache should refresh its view.
8277          * @param {Store} this
8278          */
8279         datachanged : true,
8280         /**
8281          * @event metachange
8282          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8283          * @param {Store} this
8284          * @param {Object} meta The JSON metadata
8285          */
8286         metachange : true,
8287         /**
8288          * @event add
8289          * Fires when Records have been added to the Store
8290          * @param {Store} this
8291          * @param {Roo.data.Record[]} records The array of Records added
8292          * @param {Number} index The index at which the record(s) were added
8293          */
8294         add : true,
8295         /**
8296          * @event remove
8297          * Fires when a Record has been removed from the Store
8298          * @param {Store} this
8299          * @param {Roo.data.Record} record The Record that was removed
8300          * @param {Number} index The index at which the record was removed
8301          */
8302         remove : true,
8303         /**
8304          * @event update
8305          * Fires when a Record has been updated
8306          * @param {Store} this
8307          * @param {Roo.data.Record} record The Record that was updated
8308          * @param {String} operation The update operation being performed.  Value may be one of:
8309          * <pre><code>
8310  Roo.data.Record.EDIT
8311  Roo.data.Record.REJECT
8312  Roo.data.Record.COMMIT
8313          * </code></pre>
8314          */
8315         update : true,
8316         /**
8317          * @event clear
8318          * Fires when the data cache has been cleared.
8319          * @param {Store} this
8320          */
8321         clear : true,
8322         /**
8323          * @event beforeload
8324          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8325          * the load action will be canceled.
8326          * @param {Store} this
8327          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8328          */
8329         beforeload : true,
8330         /**
8331          * @event beforeloadadd
8332          * Fires after a new set of Records has been loaded.
8333          * @param {Store} this
8334          * @param {Roo.data.Record[]} records The Records that were loaded
8335          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8336          */
8337         beforeloadadd : true,
8338         /**
8339          * @event load
8340          * Fires after a new set of Records has been loaded, before they are added to the store.
8341          * @param {Store} this
8342          * @param {Roo.data.Record[]} records The Records that were loaded
8343          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8344          * @params {Object} return from reader
8345          */
8346         load : true,
8347         /**
8348          * @event loadexception
8349          * Fires if an exception occurs in the Proxy during loading.
8350          * Called with the signature of the Proxy's "loadexception" event.
8351          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8352          * 
8353          * @param {Proxy} 
8354          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8355          * @param {Object} load options 
8356          * @param {Object} jsonData from your request (normally this contains the Exception)
8357          */
8358         loadexception : true
8359     });
8360     
8361     if(this.proxy){
8362         this.proxy = Roo.factory(this.proxy, Roo.data);
8363         this.proxy.xmodule = this.xmodule || false;
8364         this.relayEvents(this.proxy,  ["loadexception"]);
8365     }
8366     this.sortToggle = {};
8367     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8368
8369     Roo.data.Store.superclass.constructor.call(this);
8370
8371     if(this.inlineData){
8372         this.loadData(this.inlineData);
8373         delete this.inlineData;
8374     }
8375 };
8376
8377 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8378      /**
8379     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8380     * without a remote query - used by combo/forms at present.
8381     */
8382     
8383     /**
8384     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8385     */
8386     /**
8387     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8388     */
8389     /**
8390     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8391     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8392     */
8393     /**
8394     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8395     * on any HTTP request
8396     */
8397     /**
8398     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8399     */
8400     /**
8401     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8402     */
8403     multiSort: false,
8404     /**
8405     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8406     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8407     */
8408     remoteSort : false,
8409
8410     /**
8411     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8412      * loaded or when a record is removed. (defaults to false).
8413     */
8414     pruneModifiedRecords : false,
8415
8416     // private
8417     lastOptions : null,
8418
8419     /**
8420      * Add Records to the Store and fires the add event.
8421      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8422      */
8423     add : function(records){
8424         records = [].concat(records);
8425         for(var i = 0, len = records.length; i < len; i++){
8426             records[i].join(this);
8427         }
8428         var index = this.data.length;
8429         this.data.addAll(records);
8430         this.fireEvent("add", this, records, index);
8431     },
8432
8433     /**
8434      * Remove a Record from the Store and fires the remove event.
8435      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8436      */
8437     remove : function(record){
8438         var index = this.data.indexOf(record);
8439         this.data.removeAt(index);
8440         if(this.pruneModifiedRecords){
8441             this.modified.remove(record);
8442         }
8443         this.fireEvent("remove", this, record, index);
8444     },
8445
8446     /**
8447      * Remove all Records from the Store and fires the clear event.
8448      */
8449     removeAll : function(){
8450         this.data.clear();
8451         if(this.pruneModifiedRecords){
8452             this.modified = [];
8453         }
8454         this.fireEvent("clear", this);
8455     },
8456
8457     /**
8458      * Inserts Records to the Store at the given index and fires the add event.
8459      * @param {Number} index The start index at which to insert the passed Records.
8460      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8461      */
8462     insert : function(index, records){
8463         records = [].concat(records);
8464         for(var i = 0, len = records.length; i < len; i++){
8465             this.data.insert(index, records[i]);
8466             records[i].join(this);
8467         }
8468         this.fireEvent("add", this, records, index);
8469     },
8470
8471     /**
8472      * Get the index within the cache of the passed Record.
8473      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8474      * @return {Number} The index of the passed Record. Returns -1 if not found.
8475      */
8476     indexOf : function(record){
8477         return this.data.indexOf(record);
8478     },
8479
8480     /**
8481      * Get the index within the cache of the Record with the passed id.
8482      * @param {String} id The id of the Record to find.
8483      * @return {Number} The index of the Record. Returns -1 if not found.
8484      */
8485     indexOfId : function(id){
8486         return this.data.indexOfKey(id);
8487     },
8488
8489     /**
8490      * Get the Record with the specified id.
8491      * @param {String} id The id of the Record to find.
8492      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8493      */
8494     getById : function(id){
8495         return this.data.key(id);
8496     },
8497
8498     /**
8499      * Get the Record at the specified index.
8500      * @param {Number} index The index of the Record to find.
8501      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8502      */
8503     getAt : function(index){
8504         return this.data.itemAt(index);
8505     },
8506
8507     /**
8508      * Returns a range of Records between specified indices.
8509      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8510      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8511      * @return {Roo.data.Record[]} An array of Records
8512      */
8513     getRange : function(start, end){
8514         return this.data.getRange(start, end);
8515     },
8516
8517     // private
8518     storeOptions : function(o){
8519         o = Roo.apply({}, o);
8520         delete o.callback;
8521         delete o.scope;
8522         this.lastOptions = o;
8523     },
8524
8525     /**
8526      * Loads the Record cache from the configured Proxy using the configured Reader.
8527      * <p>
8528      * If using remote paging, then the first load call must specify the <em>start</em>
8529      * and <em>limit</em> properties in the options.params property to establish the initial
8530      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8531      * <p>
8532      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8533      * and this call will return before the new data has been loaded. Perform any post-processing
8534      * in a callback function, or in a "load" event handler.</strong>
8535      * <p>
8536      * @param {Object} options An object containing properties which control loading options:<ul>
8537      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8538      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8539      * passed the following arguments:<ul>
8540      * <li>r : Roo.data.Record[]</li>
8541      * <li>options: Options object from the load call</li>
8542      * <li>success: Boolean success indicator</li></ul></li>
8543      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8544      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8545      * </ul>
8546      */
8547     load : function(options){
8548         options = options || {};
8549         if(this.fireEvent("beforeload", this, options) !== false){
8550             this.storeOptions(options);
8551             var p = Roo.apply(options.params || {}, this.baseParams);
8552             // if meta was not loaded from remote source.. try requesting it.
8553             if (!this.reader.metaFromRemote) {
8554                 p._requestMeta = 1;
8555             }
8556             if(this.sortInfo && this.remoteSort){
8557                 var pn = this.paramNames;
8558                 p[pn["sort"]] = this.sortInfo.field;
8559                 p[pn["dir"]] = this.sortInfo.direction;
8560             }
8561             if (this.multiSort) {
8562                 var pn = this.paramNames;
8563                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8564             }
8565             
8566             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8567         }
8568     },
8569
8570     /**
8571      * Reloads the Record cache from the configured Proxy using the configured Reader and
8572      * the options from the last load operation performed.
8573      * @param {Object} options (optional) An object containing properties which may override the options
8574      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8575      * the most recently used options are reused).
8576      */
8577     reload : function(options){
8578         this.load(Roo.applyIf(options||{}, this.lastOptions));
8579     },
8580
8581     // private
8582     // Called as a callback by the Reader during a load operation.
8583     loadRecords : function(o, options, success){
8584         if(!o || success === false){
8585             if(success !== false){
8586                 this.fireEvent("load", this, [], options, o);
8587             }
8588             if(options.callback){
8589                 options.callback.call(options.scope || this, [], options, false);
8590             }
8591             return;
8592         }
8593         // if data returned failure - throw an exception.
8594         if (o.success === false) {
8595             // show a message if no listener is registered.
8596             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8597                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8598             }
8599             // loadmask wil be hooked into this..
8600             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8601             return;
8602         }
8603         var r = o.records, t = o.totalRecords || r.length;
8604         
8605         this.fireEvent("beforeloadadd", this, r, options, o);
8606         
8607         if(!options || options.add !== true){
8608             if(this.pruneModifiedRecords){
8609                 this.modified = [];
8610             }
8611             for(var i = 0, len = r.length; i < len; i++){
8612                 r[i].join(this);
8613             }
8614             if(this.snapshot){
8615                 this.data = this.snapshot;
8616                 delete this.snapshot;
8617             }
8618             this.data.clear();
8619             this.data.addAll(r);
8620             this.totalLength = t;
8621             this.applySort();
8622             this.fireEvent("datachanged", this);
8623         }else{
8624             this.totalLength = Math.max(t, this.data.length+r.length);
8625             this.add(r);
8626         }
8627         this.fireEvent("load", this, r, options, o);
8628         if(options.callback){
8629             options.callback.call(options.scope || this, r, options, true);
8630         }
8631     },
8632
8633
8634     /**
8635      * Loads data from a passed data block. A Reader which understands the format of the data
8636      * must have been configured in the constructor.
8637      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8638      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8639      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8640      */
8641     loadData : function(o, append){
8642         var r = this.reader.readRecords(o);
8643         this.loadRecords(r, {add: append}, true);
8644     },
8645
8646     /**
8647      * Gets the number of cached records.
8648      * <p>
8649      * <em>If using paging, this may not be the total size of the dataset. If the data object
8650      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8651      * the data set size</em>
8652      */
8653     getCount : function(){
8654         return this.data.length || 0;
8655     },
8656
8657     /**
8658      * Gets the total number of records in the dataset as returned by the server.
8659      * <p>
8660      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8661      * the dataset size</em>
8662      */
8663     getTotalCount : function(){
8664         return this.totalLength || 0;
8665     },
8666
8667     /**
8668      * Returns the sort state of the Store as an object with two properties:
8669      * <pre><code>
8670  field {String} The name of the field by which the Records are sorted
8671  direction {String} The sort order, "ASC" or "DESC"
8672      * </code></pre>
8673      */
8674     getSortState : function(){
8675         return this.sortInfo;
8676     },
8677
8678     // private
8679     applySort : function(){
8680         if(this.sortInfo && !this.remoteSort){
8681             var s = this.sortInfo, f = s.field;
8682             var st = this.fields.get(f).sortType;
8683             var fn = function(r1, r2){
8684                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8685                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8686             };
8687             this.data.sort(s.direction, fn);
8688             if(this.snapshot && this.snapshot != this.data){
8689                 this.snapshot.sort(s.direction, fn);
8690             }
8691         }
8692     },
8693
8694     /**
8695      * Sets the default sort column and order to be used by the next load operation.
8696      * @param {String} fieldName The name of the field to sort by.
8697      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8698      */
8699     setDefaultSort : function(field, dir){
8700         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8701     },
8702
8703     /**
8704      * Sort the Records.
8705      * If remote sorting is used, the sort is performed on the server, and the cache is
8706      * reloaded. If local sorting is used, the cache is sorted internally.
8707      * @param {String} fieldName The name of the field to sort by.
8708      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8709      */
8710     sort : function(fieldName, dir){
8711         var f = this.fields.get(fieldName);
8712         if(!dir){
8713             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8714             
8715             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8716                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8717             }else{
8718                 dir = f.sortDir;
8719             }
8720         }
8721         this.sortToggle[f.name] = dir;
8722         this.sortInfo = {field: f.name, direction: dir};
8723         if(!this.remoteSort){
8724             this.applySort();
8725             this.fireEvent("datachanged", this);
8726         }else{
8727             this.load(this.lastOptions);
8728         }
8729     },
8730
8731     /**
8732      * Calls the specified function for each of the Records in the cache.
8733      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8734      * Returning <em>false</em> aborts and exits the iteration.
8735      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8736      */
8737     each : function(fn, scope){
8738         this.data.each(fn, scope);
8739     },
8740
8741     /**
8742      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8743      * (e.g., during paging).
8744      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8745      */
8746     getModifiedRecords : function(){
8747         return this.modified;
8748     },
8749
8750     // private
8751     createFilterFn : function(property, value, anyMatch){
8752         if(!value.exec){ // not a regex
8753             value = String(value);
8754             if(value.length == 0){
8755                 return false;
8756             }
8757             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8758         }
8759         return function(r){
8760             return value.test(r.data[property]);
8761         };
8762     },
8763
8764     /**
8765      * Sums the value of <i>property</i> for each record between start and end and returns the result.
8766      * @param {String} property A field on your records
8767      * @param {Number} start The record index to start at (defaults to 0)
8768      * @param {Number} end The last record index to include (defaults to length - 1)
8769      * @return {Number} The sum
8770      */
8771     sum : function(property, start, end){
8772         var rs = this.data.items, v = 0;
8773         start = start || 0;
8774         end = (end || end === 0) ? end : rs.length-1;
8775
8776         for(var i = start; i <= end; i++){
8777             v += (rs[i].data[property] || 0);
8778         }
8779         return v;
8780     },
8781
8782     /**
8783      * Filter the records by a specified property.
8784      * @param {String} field A field on your records
8785      * @param {String/RegExp} value Either a string that the field
8786      * should start with or a RegExp to test against the field
8787      * @param {Boolean} anyMatch True to match any part not just the beginning
8788      */
8789     filter : function(property, value, anyMatch){
8790         var fn = this.createFilterFn(property, value, anyMatch);
8791         return fn ? this.filterBy(fn) : this.clearFilter();
8792     },
8793
8794     /**
8795      * Filter by a function. The specified function will be called with each
8796      * record in this data source. If the function returns true the record is included,
8797      * otherwise it is filtered.
8798      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8799      * @param {Object} scope (optional) The scope of the function (defaults to this)
8800      */
8801     filterBy : function(fn, scope){
8802         this.snapshot = this.snapshot || this.data;
8803         this.data = this.queryBy(fn, scope||this);
8804         this.fireEvent("datachanged", this);
8805     },
8806
8807     /**
8808      * Query the records by a specified property.
8809      * @param {String} field A field on your records
8810      * @param {String/RegExp} value Either a string that the field
8811      * should start with or a RegExp to test against the field
8812      * @param {Boolean} anyMatch True to match any part not just the beginning
8813      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8814      */
8815     query : function(property, value, anyMatch){
8816         var fn = this.createFilterFn(property, value, anyMatch);
8817         return fn ? this.queryBy(fn) : this.data.clone();
8818     },
8819
8820     /**
8821      * Query by a function. The specified function will be called with each
8822      * record in this data source. If the function returns true the record is included
8823      * in the results.
8824      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8825      * @param {Object} scope (optional) The scope of the function (defaults to this)
8826       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8827      **/
8828     queryBy : function(fn, scope){
8829         var data = this.snapshot || this.data;
8830         return data.filterBy(fn, scope||this);
8831     },
8832
8833     /**
8834      * Collects unique values for a particular dataIndex from this store.
8835      * @param {String} dataIndex The property to collect
8836      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8837      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8838      * @return {Array} An array of the unique values
8839      **/
8840     collect : function(dataIndex, allowNull, bypassFilter){
8841         var d = (bypassFilter === true && this.snapshot) ?
8842                 this.snapshot.items : this.data.items;
8843         var v, sv, r = [], l = {};
8844         for(var i = 0, len = d.length; i < len; i++){
8845             v = d[i].data[dataIndex];
8846             sv = String(v);
8847             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
8848                 l[sv] = true;
8849                 r[r.length] = v;
8850             }
8851         }
8852         return r;
8853     },
8854
8855     /**
8856      * Revert to a view of the Record cache with no filtering applied.
8857      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
8858      */
8859     clearFilter : function(suppressEvent){
8860         if(this.snapshot && this.snapshot != this.data){
8861             this.data = this.snapshot;
8862             delete this.snapshot;
8863             if(suppressEvent !== true){
8864                 this.fireEvent("datachanged", this);
8865             }
8866         }
8867     },
8868
8869     // private
8870     afterEdit : function(record){
8871         if(this.modified.indexOf(record) == -1){
8872             this.modified.push(record);
8873         }
8874         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
8875     },
8876     
8877     // private
8878     afterReject : function(record){
8879         this.modified.remove(record);
8880         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
8881     },
8882
8883     // private
8884     afterCommit : function(record){
8885         this.modified.remove(record);
8886         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
8887     },
8888
8889     /**
8890      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
8891      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
8892      */
8893     commitChanges : function(){
8894         var m = this.modified.slice(0);
8895         this.modified = [];
8896         for(var i = 0, len = m.length; i < len; i++){
8897             m[i].commit();
8898         }
8899     },
8900
8901     /**
8902      * Cancel outstanding changes on all changed records.
8903      */
8904     rejectChanges : function(){
8905         var m = this.modified.slice(0);
8906         this.modified = [];
8907         for(var i = 0, len = m.length; i < len; i++){
8908             m[i].reject();
8909         }
8910     },
8911
8912     onMetaChange : function(meta, rtype, o){
8913         this.recordType = rtype;
8914         this.fields = rtype.prototype.fields;
8915         delete this.snapshot;
8916         this.sortInfo = meta.sortInfo || this.sortInfo;
8917         this.modified = [];
8918         this.fireEvent('metachange', this, this.reader.meta);
8919     },
8920     
8921     moveIndex : function(data, type)
8922     {
8923         var index = this.indexOf(data);
8924         
8925         var newIndex = index + type;
8926         
8927         this.remove(data);
8928         
8929         this.insert(newIndex, data);
8930         
8931     }
8932 });/*
8933  * Based on:
8934  * Ext JS Library 1.1.1
8935  * Copyright(c) 2006-2007, Ext JS, LLC.
8936  *
8937  * Originally Released Under LGPL - original licence link has changed is not relivant.
8938  *
8939  * Fork - LGPL
8940  * <script type="text/javascript">
8941  */
8942
8943 /**
8944  * @class Roo.data.SimpleStore
8945  * @extends Roo.data.Store
8946  * Small helper class to make creating Stores from Array data easier.
8947  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
8948  * @cfg {Array} fields An array of field definition objects, or field name strings.
8949  * @cfg {Array} data The multi-dimensional array of data
8950  * @constructor
8951  * @param {Object} config
8952  */
8953 Roo.data.SimpleStore = function(config){
8954     Roo.data.SimpleStore.superclass.constructor.call(this, {
8955         isLocal : true,
8956         reader: new Roo.data.ArrayReader({
8957                 id: config.id
8958             },
8959             Roo.data.Record.create(config.fields)
8960         ),
8961         proxy : new Roo.data.MemoryProxy(config.data)
8962     });
8963     this.load();
8964 };
8965 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
8966  * Based on:
8967  * Ext JS Library 1.1.1
8968  * Copyright(c) 2006-2007, Ext JS, LLC.
8969  *
8970  * Originally Released Under LGPL - original licence link has changed is not relivant.
8971  *
8972  * Fork - LGPL
8973  * <script type="text/javascript">
8974  */
8975
8976 /**
8977 /**
8978  * @extends Roo.data.Store
8979  * @class Roo.data.JsonStore
8980  * Small helper class to make creating Stores for JSON data easier. <br/>
8981 <pre><code>
8982 var store = new Roo.data.JsonStore({
8983     url: 'get-images.php',
8984     root: 'images',
8985     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
8986 });
8987 </code></pre>
8988  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
8989  * JsonReader and HttpProxy (unless inline data is provided).</b>
8990  * @cfg {Array} fields An array of field definition objects, or field name strings.
8991  * @constructor
8992  * @param {Object} config
8993  */
8994 Roo.data.JsonStore = function(c){
8995     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
8996         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
8997         reader: new Roo.data.JsonReader(c, c.fields)
8998     }));
8999 };
9000 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9001  * Based on:
9002  * Ext JS Library 1.1.1
9003  * Copyright(c) 2006-2007, Ext JS, LLC.
9004  *
9005  * Originally Released Under LGPL - original licence link has changed is not relivant.
9006  *
9007  * Fork - LGPL
9008  * <script type="text/javascript">
9009  */
9010
9011  
9012 Roo.data.Field = function(config){
9013     if(typeof config == "string"){
9014         config = {name: config};
9015     }
9016     Roo.apply(this, config);
9017     
9018     if(!this.type){
9019         this.type = "auto";
9020     }
9021     
9022     var st = Roo.data.SortTypes;
9023     // named sortTypes are supported, here we look them up
9024     if(typeof this.sortType == "string"){
9025         this.sortType = st[this.sortType];
9026     }
9027     
9028     // set default sortType for strings and dates
9029     if(!this.sortType){
9030         switch(this.type){
9031             case "string":
9032                 this.sortType = st.asUCString;
9033                 break;
9034             case "date":
9035                 this.sortType = st.asDate;
9036                 break;
9037             default:
9038                 this.sortType = st.none;
9039         }
9040     }
9041
9042     // define once
9043     var stripRe = /[\$,%]/g;
9044
9045     // prebuilt conversion function for this field, instead of
9046     // switching every time we're reading a value
9047     if(!this.convert){
9048         var cv, dateFormat = this.dateFormat;
9049         switch(this.type){
9050             case "":
9051             case "auto":
9052             case undefined:
9053                 cv = function(v){ return v; };
9054                 break;
9055             case "string":
9056                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9057                 break;
9058             case "int":
9059                 cv = function(v){
9060                     return v !== undefined && v !== null && v !== '' ?
9061                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9062                     };
9063                 break;
9064             case "float":
9065                 cv = function(v){
9066                     return v !== undefined && v !== null && v !== '' ?
9067                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9068                     };
9069                 break;
9070             case "bool":
9071             case "boolean":
9072                 cv = function(v){ return v === true || v === "true" || v == 1; };
9073                 break;
9074             case "date":
9075                 cv = function(v){
9076                     if(!v){
9077                         return '';
9078                     }
9079                     if(v instanceof Date){
9080                         return v;
9081                     }
9082                     if(dateFormat){
9083                         if(dateFormat == "timestamp"){
9084                             return new Date(v*1000);
9085                         }
9086                         return Date.parseDate(v, dateFormat);
9087                     }
9088                     var parsed = Date.parse(v);
9089                     return parsed ? new Date(parsed) : null;
9090                 };
9091              break;
9092             
9093         }
9094         this.convert = cv;
9095     }
9096 };
9097
9098 Roo.data.Field.prototype = {
9099     dateFormat: null,
9100     defaultValue: "",
9101     mapping: null,
9102     sortType : null,
9103     sortDir : "ASC"
9104 };/*
9105  * Based on:
9106  * Ext JS Library 1.1.1
9107  * Copyright(c) 2006-2007, Ext JS, LLC.
9108  *
9109  * Originally Released Under LGPL - original licence link has changed is not relivant.
9110  *
9111  * Fork - LGPL
9112  * <script type="text/javascript">
9113  */
9114  
9115 // Base class for reading structured data from a data source.  This class is intended to be
9116 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9117
9118 /**
9119  * @class Roo.data.DataReader
9120  * Base class for reading structured data from a data source.  This class is intended to be
9121  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9122  */
9123
9124 Roo.data.DataReader = function(meta, recordType){
9125     
9126     this.meta = meta;
9127     
9128     this.recordType = recordType instanceof Array ? 
9129         Roo.data.Record.create(recordType) : recordType;
9130 };
9131
9132 Roo.data.DataReader.prototype = {
9133      /**
9134      * Create an empty record
9135      * @param {Object} data (optional) - overlay some values
9136      * @return {Roo.data.Record} record created.
9137      */
9138     newRow :  function(d) {
9139         var da =  {};
9140         this.recordType.prototype.fields.each(function(c) {
9141             switch( c.type) {
9142                 case 'int' : da[c.name] = 0; break;
9143                 case 'date' : da[c.name] = new Date(); break;
9144                 case 'float' : da[c.name] = 0.0; break;
9145                 case 'boolean' : da[c.name] = false; break;
9146                 default : da[c.name] = ""; break;
9147             }
9148             
9149         });
9150         return new this.recordType(Roo.apply(da, d));
9151     }
9152     
9153 };/*
9154  * Based on:
9155  * Ext JS Library 1.1.1
9156  * Copyright(c) 2006-2007, Ext JS, LLC.
9157  *
9158  * Originally Released Under LGPL - original licence link has changed is not relivant.
9159  *
9160  * Fork - LGPL
9161  * <script type="text/javascript">
9162  */
9163
9164 /**
9165  * @class Roo.data.DataProxy
9166  * @extends Roo.data.Observable
9167  * This class is an abstract base class for implementations which provide retrieval of
9168  * unformatted data objects.<br>
9169  * <p>
9170  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9171  * (of the appropriate type which knows how to parse the data object) to provide a block of
9172  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9173  * <p>
9174  * Custom implementations must implement the load method as described in
9175  * {@link Roo.data.HttpProxy#load}.
9176  */
9177 Roo.data.DataProxy = function(){
9178     this.addEvents({
9179         /**
9180          * @event beforeload
9181          * Fires before a network request is made to retrieve a data object.
9182          * @param {Object} This DataProxy object.
9183          * @param {Object} params The params parameter to the load function.
9184          */
9185         beforeload : true,
9186         /**
9187          * @event load
9188          * Fires before the load method's callback is called.
9189          * @param {Object} This DataProxy object.
9190          * @param {Object} o The data object.
9191          * @param {Object} arg The callback argument object passed to the load function.
9192          */
9193         load : true,
9194         /**
9195          * @event loadexception
9196          * Fires if an Exception occurs during data retrieval.
9197          * @param {Object} This DataProxy object.
9198          * @param {Object} o The data object.
9199          * @param {Object} arg The callback argument object passed to the load function.
9200          * @param {Object} e The Exception.
9201          */
9202         loadexception : true
9203     });
9204     Roo.data.DataProxy.superclass.constructor.call(this);
9205 };
9206
9207 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9208
9209     /**
9210      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9211      */
9212 /*
9213  * Based on:
9214  * Ext JS Library 1.1.1
9215  * Copyright(c) 2006-2007, Ext JS, LLC.
9216  *
9217  * Originally Released Under LGPL - original licence link has changed is not relivant.
9218  *
9219  * Fork - LGPL
9220  * <script type="text/javascript">
9221  */
9222 /**
9223  * @class Roo.data.MemoryProxy
9224  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9225  * to the Reader when its load method is called.
9226  * @constructor
9227  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9228  */
9229 Roo.data.MemoryProxy = function(data){
9230     if (data.data) {
9231         data = data.data;
9232     }
9233     Roo.data.MemoryProxy.superclass.constructor.call(this);
9234     this.data = data;
9235 };
9236
9237 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9238     /**
9239      * Load data from the requested source (in this case an in-memory
9240      * data object passed to the constructor), read the data object into
9241      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9242      * process that block using the passed callback.
9243      * @param {Object} params This parameter is not used by the MemoryProxy class.
9244      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9245      * object into a block of Roo.data.Records.
9246      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9247      * The function must be passed <ul>
9248      * <li>The Record block object</li>
9249      * <li>The "arg" argument from the load function</li>
9250      * <li>A boolean success indicator</li>
9251      * </ul>
9252      * @param {Object} scope The scope in which to call the callback
9253      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9254      */
9255     load : function(params, reader, callback, scope, arg){
9256         params = params || {};
9257         var result;
9258         try {
9259             result = reader.readRecords(this.data);
9260         }catch(e){
9261             this.fireEvent("loadexception", this, arg, null, e);
9262             callback.call(scope, null, arg, false);
9263             return;
9264         }
9265         callback.call(scope, result, arg, true);
9266     },
9267     
9268     // private
9269     update : function(params, records){
9270         
9271     }
9272 });/*
9273  * Based on:
9274  * Ext JS Library 1.1.1
9275  * Copyright(c) 2006-2007, Ext JS, LLC.
9276  *
9277  * Originally Released Under LGPL - original licence link has changed is not relivant.
9278  *
9279  * Fork - LGPL
9280  * <script type="text/javascript">
9281  */
9282 /**
9283  * @class Roo.data.HttpProxy
9284  * @extends Roo.data.DataProxy
9285  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9286  * configured to reference a certain URL.<br><br>
9287  * <p>
9288  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9289  * from which the running page was served.<br><br>
9290  * <p>
9291  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9292  * <p>
9293  * Be aware that to enable the browser to parse an XML document, the server must set
9294  * the Content-Type header in the HTTP response to "text/xml".
9295  * @constructor
9296  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9297  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9298  * will be used to make the request.
9299  */
9300 Roo.data.HttpProxy = function(conn){
9301     Roo.data.HttpProxy.superclass.constructor.call(this);
9302     // is conn a conn config or a real conn?
9303     this.conn = conn;
9304     this.useAjax = !conn || !conn.events;
9305   
9306 };
9307
9308 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9309     // thse are take from connection...
9310     
9311     /**
9312      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9313      */
9314     /**
9315      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9316      * extra parameters to each request made by this object. (defaults to undefined)
9317      */
9318     /**
9319      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9320      *  to each request made by this object. (defaults to undefined)
9321      */
9322     /**
9323      * @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)
9324      */
9325     /**
9326      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9327      */
9328      /**
9329      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9330      * @type Boolean
9331      */
9332   
9333
9334     /**
9335      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9336      * @type Boolean
9337      */
9338     /**
9339      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9340      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9341      * a finer-grained basis than the DataProxy events.
9342      */
9343     getConnection : function(){
9344         return this.useAjax ? Roo.Ajax : this.conn;
9345     },
9346
9347     /**
9348      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9349      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9350      * process that block using the passed callback.
9351      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9352      * for the request to the remote server.
9353      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9354      * object into a block of Roo.data.Records.
9355      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9356      * The function must be passed <ul>
9357      * <li>The Record block object</li>
9358      * <li>The "arg" argument from the load function</li>
9359      * <li>A boolean success indicator</li>
9360      * </ul>
9361      * @param {Object} scope The scope in which to call the callback
9362      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9363      */
9364     load : function(params, reader, callback, scope, arg){
9365         if(this.fireEvent("beforeload", this, params) !== false){
9366             var  o = {
9367                 params : params || {},
9368                 request: {
9369                     callback : callback,
9370                     scope : scope,
9371                     arg : arg
9372                 },
9373                 reader: reader,
9374                 callback : this.loadResponse,
9375                 scope: this
9376             };
9377             if(this.useAjax){
9378                 Roo.applyIf(o, this.conn);
9379                 if(this.activeRequest){
9380                     Roo.Ajax.abort(this.activeRequest);
9381                 }
9382                 this.activeRequest = Roo.Ajax.request(o);
9383             }else{
9384                 this.conn.request(o);
9385             }
9386         }else{
9387             callback.call(scope||this, null, arg, false);
9388         }
9389     },
9390
9391     // private
9392     loadResponse : function(o, success, response){
9393         delete this.activeRequest;
9394         if(!success){
9395             this.fireEvent("loadexception", this, o, response);
9396             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9397             return;
9398         }
9399         var result;
9400         try {
9401             result = o.reader.read(response);
9402         }catch(e){
9403             this.fireEvent("loadexception", this, o, response, e);
9404             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9405             return;
9406         }
9407         
9408         this.fireEvent("load", this, o, o.request.arg);
9409         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9410     },
9411
9412     // private
9413     update : function(dataSet){
9414
9415     },
9416
9417     // private
9418     updateResponse : function(dataSet){
9419
9420     }
9421 });/*
9422  * Based on:
9423  * Ext JS Library 1.1.1
9424  * Copyright(c) 2006-2007, Ext JS, LLC.
9425  *
9426  * Originally Released Under LGPL - original licence link has changed is not relivant.
9427  *
9428  * Fork - LGPL
9429  * <script type="text/javascript">
9430  */
9431
9432 /**
9433  * @class Roo.data.ScriptTagProxy
9434  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9435  * other than the originating domain of the running page.<br><br>
9436  * <p>
9437  * <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
9438  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9439  * <p>
9440  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9441  * source code that is used as the source inside a &lt;script> tag.<br><br>
9442  * <p>
9443  * In order for the browser to process the returned data, the server must wrap the data object
9444  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9445  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9446  * depending on whether the callback name was passed:
9447  * <p>
9448  * <pre><code>
9449 boolean scriptTag = false;
9450 String cb = request.getParameter("callback");
9451 if (cb != null) {
9452     scriptTag = true;
9453     response.setContentType("text/javascript");
9454 } else {
9455     response.setContentType("application/x-json");
9456 }
9457 Writer out = response.getWriter();
9458 if (scriptTag) {
9459     out.write(cb + "(");
9460 }
9461 out.print(dataBlock.toJsonString());
9462 if (scriptTag) {
9463     out.write(");");
9464 }
9465 </pre></code>
9466  *
9467  * @constructor
9468  * @param {Object} config A configuration object.
9469  */
9470 Roo.data.ScriptTagProxy = function(config){
9471     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9472     Roo.apply(this, config);
9473     this.head = document.getElementsByTagName("head")[0];
9474 };
9475
9476 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9477
9478 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9479     /**
9480      * @cfg {String} url The URL from which to request the data object.
9481      */
9482     /**
9483      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9484      */
9485     timeout : 30000,
9486     /**
9487      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9488      * the server the name of the callback function set up by the load call to process the returned data object.
9489      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9490      * javascript output which calls this named function passing the data object as its only parameter.
9491      */
9492     callbackParam : "callback",
9493     /**
9494      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9495      * name to the request.
9496      */
9497     nocache : true,
9498
9499     /**
9500      * Load data from the configured URL, read the data object into
9501      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9502      * process that block using the passed callback.
9503      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9504      * for the request to the remote server.
9505      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9506      * object into a block of Roo.data.Records.
9507      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9508      * The function must be passed <ul>
9509      * <li>The Record block object</li>
9510      * <li>The "arg" argument from the load function</li>
9511      * <li>A boolean success indicator</li>
9512      * </ul>
9513      * @param {Object} scope The scope in which to call the callback
9514      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9515      */
9516     load : function(params, reader, callback, scope, arg){
9517         if(this.fireEvent("beforeload", this, params) !== false){
9518
9519             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9520
9521             var url = this.url;
9522             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9523             if(this.nocache){
9524                 url += "&_dc=" + (new Date().getTime());
9525             }
9526             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9527             var trans = {
9528                 id : transId,
9529                 cb : "stcCallback"+transId,
9530                 scriptId : "stcScript"+transId,
9531                 params : params,
9532                 arg : arg,
9533                 url : url,
9534                 callback : callback,
9535                 scope : scope,
9536                 reader : reader
9537             };
9538             var conn = this;
9539
9540             window[trans.cb] = function(o){
9541                 conn.handleResponse(o, trans);
9542             };
9543
9544             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9545
9546             if(this.autoAbort !== false){
9547                 this.abort();
9548             }
9549
9550             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9551
9552             var script = document.createElement("script");
9553             script.setAttribute("src", url);
9554             script.setAttribute("type", "text/javascript");
9555             script.setAttribute("id", trans.scriptId);
9556             this.head.appendChild(script);
9557
9558             this.trans = trans;
9559         }else{
9560             callback.call(scope||this, null, arg, false);
9561         }
9562     },
9563
9564     // private
9565     isLoading : function(){
9566         return this.trans ? true : false;
9567     },
9568
9569     /**
9570      * Abort the current server request.
9571      */
9572     abort : function(){
9573         if(this.isLoading()){
9574             this.destroyTrans(this.trans);
9575         }
9576     },
9577
9578     // private
9579     destroyTrans : function(trans, isLoaded){
9580         this.head.removeChild(document.getElementById(trans.scriptId));
9581         clearTimeout(trans.timeoutId);
9582         if(isLoaded){
9583             window[trans.cb] = undefined;
9584             try{
9585                 delete window[trans.cb];
9586             }catch(e){}
9587         }else{
9588             // if hasn't been loaded, wait for load to remove it to prevent script error
9589             window[trans.cb] = function(){
9590                 window[trans.cb] = undefined;
9591                 try{
9592                     delete window[trans.cb];
9593                 }catch(e){}
9594             };
9595         }
9596     },
9597
9598     // private
9599     handleResponse : function(o, trans){
9600         this.trans = false;
9601         this.destroyTrans(trans, true);
9602         var result;
9603         try {
9604             result = trans.reader.readRecords(o);
9605         }catch(e){
9606             this.fireEvent("loadexception", this, o, trans.arg, e);
9607             trans.callback.call(trans.scope||window, null, trans.arg, false);
9608             return;
9609         }
9610         this.fireEvent("load", this, o, trans.arg);
9611         trans.callback.call(trans.scope||window, result, trans.arg, true);
9612     },
9613
9614     // private
9615     handleFailure : function(trans){
9616         this.trans = false;
9617         this.destroyTrans(trans, false);
9618         this.fireEvent("loadexception", this, null, trans.arg);
9619         trans.callback.call(trans.scope||window, null, trans.arg, false);
9620     }
9621 });/*
9622  * Based on:
9623  * Ext JS Library 1.1.1
9624  * Copyright(c) 2006-2007, Ext JS, LLC.
9625  *
9626  * Originally Released Under LGPL - original licence link has changed is not relivant.
9627  *
9628  * Fork - LGPL
9629  * <script type="text/javascript">
9630  */
9631
9632 /**
9633  * @class Roo.data.JsonReader
9634  * @extends Roo.data.DataReader
9635  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9636  * based on mappings in a provided Roo.data.Record constructor.
9637  * 
9638  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9639  * in the reply previously. 
9640  * 
9641  * <p>
9642  * Example code:
9643  * <pre><code>
9644 var RecordDef = Roo.data.Record.create([
9645     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9646     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9647 ]);
9648 var myReader = new Roo.data.JsonReader({
9649     totalProperty: "results",    // The property which contains the total dataset size (optional)
9650     root: "rows",                // The property which contains an Array of row objects
9651     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9652 }, RecordDef);
9653 </code></pre>
9654  * <p>
9655  * This would consume a JSON file like this:
9656  * <pre><code>
9657 { 'results': 2, 'rows': [
9658     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9659     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9660 }
9661 </code></pre>
9662  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9663  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9664  * paged from the remote server.
9665  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9666  * @cfg {String} root name of the property which contains the Array of row objects.
9667  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9668  * @constructor
9669  * Create a new JsonReader
9670  * @param {Object} meta Metadata configuration options
9671  * @param {Object} recordType Either an Array of field definition objects,
9672  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9673  */
9674 Roo.data.JsonReader = function(meta, recordType){
9675     
9676     meta = meta || {};
9677     // set some defaults:
9678     Roo.applyIf(meta, {
9679         totalProperty: 'total',
9680         successProperty : 'success',
9681         root : 'data',
9682         id : 'id'
9683     });
9684     
9685     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9686 };
9687 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9688     
9689     /**
9690      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9691      * Used by Store query builder to append _requestMeta to params.
9692      * 
9693      */
9694     metaFromRemote : false,
9695     /**
9696      * This method is only used by a DataProxy which has retrieved data from a remote server.
9697      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9698      * @return {Object} data A data block which is used by an Roo.data.Store object as
9699      * a cache of Roo.data.Records.
9700      */
9701     read : function(response){
9702         var json = response.responseText;
9703        
9704         var o = /* eval:var:o */ eval("("+json+")");
9705         if(!o) {
9706             throw {message: "JsonReader.read: Json object not found"};
9707         }
9708         
9709         if(o.metaData){
9710             
9711             delete this.ef;
9712             this.metaFromRemote = true;
9713             this.meta = o.metaData;
9714             this.recordType = Roo.data.Record.create(o.metaData.fields);
9715             this.onMetaChange(this.meta, this.recordType, o);
9716         }
9717         return this.readRecords(o);
9718     },
9719
9720     // private function a store will implement
9721     onMetaChange : function(meta, recordType, o){
9722
9723     },
9724
9725     /**
9726          * @ignore
9727          */
9728     simpleAccess: function(obj, subsc) {
9729         return obj[subsc];
9730     },
9731
9732         /**
9733          * @ignore
9734          */
9735     getJsonAccessor: function(){
9736         var re = /[\[\.]/;
9737         return function(expr) {
9738             try {
9739                 return(re.test(expr))
9740                     ? new Function("obj", "return obj." + expr)
9741                     : function(obj){
9742                         return obj[expr];
9743                     };
9744             } catch(e){}
9745             return Roo.emptyFn;
9746         };
9747     }(),
9748
9749     /**
9750      * Create a data block containing Roo.data.Records from an XML document.
9751      * @param {Object} o An object which contains an Array of row objects in the property specified
9752      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9753      * which contains the total size of the dataset.
9754      * @return {Object} data A data block which is used by an Roo.data.Store object as
9755      * a cache of Roo.data.Records.
9756      */
9757     readRecords : function(o){
9758         /**
9759          * After any data loads, the raw JSON data is available for further custom processing.
9760          * @type Object
9761          */
9762         this.o = o;
9763         var s = this.meta, Record = this.recordType,
9764             f = Record.prototype.fields, fi = f.items, fl = f.length;
9765
9766 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
9767         if (!this.ef) {
9768             if(s.totalProperty) {
9769                     this.getTotal = this.getJsonAccessor(s.totalProperty);
9770                 }
9771                 if(s.successProperty) {
9772                     this.getSuccess = this.getJsonAccessor(s.successProperty);
9773                 }
9774                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
9775                 if (s.id) {
9776                         var g = this.getJsonAccessor(s.id);
9777                         this.getId = function(rec) {
9778                                 var r = g(rec);
9779                                 return (r === undefined || r === "") ? null : r;
9780                         };
9781                 } else {
9782                         this.getId = function(){return null;};
9783                 }
9784             this.ef = [];
9785             for(var jj = 0; jj < fl; jj++){
9786                 f = fi[jj];
9787                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
9788                 this.ef[jj] = this.getJsonAccessor(map);
9789             }
9790         }
9791
9792         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
9793         if(s.totalProperty){
9794             var vt = parseInt(this.getTotal(o), 10);
9795             if(!isNaN(vt)){
9796                 totalRecords = vt;
9797             }
9798         }
9799         if(s.successProperty){
9800             var vs = this.getSuccess(o);
9801             if(vs === false || vs === 'false'){
9802                 success = false;
9803             }
9804         }
9805         var records = [];
9806             for(var i = 0; i < c; i++){
9807                     var n = root[i];
9808                 var values = {};
9809                 var id = this.getId(n);
9810                 for(var j = 0; j < fl; j++){
9811                     f = fi[j];
9812                 var v = this.ef[j](n);
9813                 if (!f.convert) {
9814                     Roo.log('missing convert for ' + f.name);
9815                     Roo.log(f);
9816                     continue;
9817                 }
9818                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9819                 }
9820                 var record = new Record(values, id);
9821                 record.json = n;
9822                 records[i] = record;
9823             }
9824             return {
9825             raw : o,
9826                 success : success,
9827                 records : records,
9828                 totalRecords : totalRecords
9829             };
9830     }
9831 });/*
9832  * Based on:
9833  * Ext JS Library 1.1.1
9834  * Copyright(c) 2006-2007, Ext JS, LLC.
9835  *
9836  * Originally Released Under LGPL - original licence link has changed is not relivant.
9837  *
9838  * Fork - LGPL
9839  * <script type="text/javascript">
9840  */
9841
9842 /**
9843  * @class Roo.data.ArrayReader
9844  * @extends Roo.data.DataReader
9845  * Data reader class to create an Array of Roo.data.Record objects from an Array.
9846  * Each element of that Array represents a row of data fields. The
9847  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
9848  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
9849  * <p>
9850  * Example code:.
9851  * <pre><code>
9852 var RecordDef = Roo.data.Record.create([
9853     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
9854     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
9855 ]);
9856 var myReader = new Roo.data.ArrayReader({
9857     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
9858 }, RecordDef);
9859 </code></pre>
9860  * <p>
9861  * This would consume an Array like this:
9862  * <pre><code>
9863 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
9864   </code></pre>
9865  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
9866  * @constructor
9867  * Create a new JsonReader
9868  * @param {Object} meta Metadata configuration options.
9869  * @param {Object} recordType Either an Array of field definition objects
9870  * as specified to {@link Roo.data.Record#create},
9871  * or an {@link Roo.data.Record} object
9872  * created using {@link Roo.data.Record#create}.
9873  */
9874 Roo.data.ArrayReader = function(meta, recordType){
9875     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
9876 };
9877
9878 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
9879     /**
9880      * Create a data block containing Roo.data.Records from an XML document.
9881      * @param {Object} o An Array of row objects which represents the dataset.
9882      * @return {Object} data A data block which is used by an Roo.data.Store object as
9883      * a cache of Roo.data.Records.
9884      */
9885     readRecords : function(o){
9886         var sid = this.meta ? this.meta.id : null;
9887         var recordType = this.recordType, fields = recordType.prototype.fields;
9888         var records = [];
9889         var root = o;
9890             for(var i = 0; i < root.length; i++){
9891                     var n = root[i];
9892                 var values = {};
9893                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
9894                 for(var j = 0, jlen = fields.length; j < jlen; j++){
9895                 var f = fields.items[j];
9896                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
9897                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
9898                 v = f.convert(v);
9899                 values[f.name] = v;
9900             }
9901                 var record = new recordType(values, id);
9902                 record.json = n;
9903                 records[records.length] = record;
9904             }
9905             return {
9906                 records : records,
9907                 totalRecords : records.length
9908             };
9909     }
9910 });/*
9911  * - LGPL
9912  * * 
9913  */
9914
9915 /**
9916  * @class Roo.bootstrap.ComboBox
9917  * @extends Roo.bootstrap.TriggerField
9918  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
9919  * @cfg {Boolean} append (true|false) default false
9920  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
9921  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
9922  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
9923  * @constructor
9924  * Create a new ComboBox.
9925  * @param {Object} config Configuration options
9926  */
9927 Roo.bootstrap.ComboBox = function(config){
9928     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
9929     this.addEvents({
9930         /**
9931          * @event expand
9932          * Fires when the dropdown list is expanded
9933              * @param {Roo.bootstrap.ComboBox} combo This combo box
9934              */
9935         'expand' : true,
9936         /**
9937          * @event collapse
9938          * Fires when the dropdown list is collapsed
9939              * @param {Roo.bootstrap.ComboBox} combo This combo box
9940              */
9941         'collapse' : true,
9942         /**
9943          * @event beforeselect
9944          * Fires before a list item is selected. Return false to cancel the selection.
9945              * @param {Roo.bootstrap.ComboBox} combo This combo box
9946              * @param {Roo.data.Record} record The data record returned from the underlying store
9947              * @param {Number} index The index of the selected item in the dropdown list
9948              */
9949         'beforeselect' : true,
9950         /**
9951          * @event select
9952          * Fires when a list item is selected
9953              * @param {Roo.bootstrap.ComboBox} combo This combo box
9954              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
9955              * @param {Number} index The index of the selected item in the dropdown list
9956              */
9957         'select' : true,
9958         /**
9959          * @event beforequery
9960          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
9961          * The event object passed has these properties:
9962              * @param {Roo.bootstrap.ComboBox} combo This combo box
9963              * @param {String} query The query
9964              * @param {Boolean} forceAll true to force "all" query
9965              * @param {Boolean} cancel true to cancel the query
9966              * @param {Object} e The query event object
9967              */
9968         'beforequery': true,
9969          /**
9970          * @event add
9971          * Fires when the 'add' icon is pressed (add a listener to enable add button)
9972              * @param {Roo.bootstrap.ComboBox} combo This combo box
9973              */
9974         'add' : true,
9975         /**
9976          * @event edit
9977          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9978              * @param {Roo.bootstrap.ComboBox} combo This combo box
9979              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9980              */
9981         'edit' : true,
9982         /**
9983          * @event remove
9984          * Fires when the remove value from the combobox array
9985              * @param {Roo.bootstrap.ComboBox} combo This combo box
9986              */
9987         'remove' : true
9988         
9989     });
9990     
9991     this.item = [];
9992     this.tickItems = [];
9993     
9994     this.selectedIndex = -1;
9995     if(this.mode == 'local'){
9996         if(config.queryDelay === undefined){
9997             this.queryDelay = 10;
9998         }
9999         if(config.minChars === undefined){
10000             this.minChars = 0;
10001         }
10002     }
10003 };
10004
10005 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10006      
10007     /**
10008      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10009      * rendering into an Roo.Editor, defaults to false)
10010      */
10011     /**
10012      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10013      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10014      */
10015     /**
10016      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10017      */
10018     /**
10019      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10020      * the dropdown list (defaults to undefined, with no header element)
10021      */
10022
10023      /**
10024      * @cfg {String/Roo.Template} tpl The template to use to render the output
10025      */
10026      
10027      /**
10028      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10029      */
10030     listWidth: undefined,
10031     /**
10032      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10033      * mode = 'remote' or 'text' if mode = 'local')
10034      */
10035     displayField: undefined,
10036     /**
10037      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10038      * mode = 'remote' or 'value' if mode = 'local'). 
10039      * Note: use of a valueField requires the user make a selection
10040      * in order for a value to be mapped.
10041      */
10042     valueField: undefined,
10043     
10044     
10045     /**
10046      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10047      * field's data value (defaults to the underlying DOM element's name)
10048      */
10049     hiddenName: undefined,
10050     /**
10051      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10052      */
10053     listClass: '',
10054     /**
10055      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10056      */
10057     selectedClass: 'active',
10058     
10059     /**
10060      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10061      */
10062     shadow:'sides',
10063     /**
10064      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10065      * anchor positions (defaults to 'tl-bl')
10066      */
10067     listAlign: 'tl-bl?',
10068     /**
10069      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10070      */
10071     maxHeight: 300,
10072     /**
10073      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10074      * query specified by the allQuery config option (defaults to 'query')
10075      */
10076     triggerAction: 'query',
10077     /**
10078      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10079      * (defaults to 4, does not apply if editable = false)
10080      */
10081     minChars : 4,
10082     /**
10083      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10084      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10085      */
10086     typeAhead: false,
10087     /**
10088      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10089      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10090      */
10091     queryDelay: 500,
10092     /**
10093      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10094      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10095      */
10096     pageSize: 0,
10097     /**
10098      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10099      * when editable = true (defaults to false)
10100      */
10101     selectOnFocus:false,
10102     /**
10103      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10104      */
10105     queryParam: 'query',
10106     /**
10107      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10108      * when mode = 'remote' (defaults to 'Loading...')
10109      */
10110     loadingText: 'Loading...',
10111     /**
10112      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10113      */
10114     resizable: false,
10115     /**
10116      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10117      */
10118     handleHeight : 8,
10119     /**
10120      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10121      * traditional select (defaults to true)
10122      */
10123     editable: true,
10124     /**
10125      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10126      */
10127     allQuery: '',
10128     /**
10129      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10130      */
10131     mode: 'remote',
10132     /**
10133      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10134      * listWidth has a higher value)
10135      */
10136     minListWidth : 70,
10137     /**
10138      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10139      * allow the user to set arbitrary text into the field (defaults to false)
10140      */
10141     forceSelection:false,
10142     /**
10143      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10144      * if typeAhead = true (defaults to 250)
10145      */
10146     typeAheadDelay : 250,
10147     /**
10148      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10149      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10150      */
10151     valueNotFoundText : undefined,
10152     /**
10153      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10154      */
10155     blockFocus : false,
10156     
10157     /**
10158      * @cfg {Boolean} disableClear Disable showing of clear button.
10159      */
10160     disableClear : false,
10161     /**
10162      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10163      */
10164     alwaysQuery : false,
10165     
10166     /**
10167      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10168      */
10169     multiple : false,
10170     
10171     //private
10172     addicon : false,
10173     editicon: false,
10174     
10175     page: 0,
10176     hasQuery: false,
10177     append: false,
10178     loadNext: false,
10179     autoFocus : true,
10180     tickable : false,
10181     btnPosition : 'right',
10182     
10183     // element that contains real text value.. (when hidden is used..)
10184     
10185     getAutoCreate : function()
10186     {
10187         var cfg = false;
10188         
10189         /*
10190          *  Normal ComboBox
10191          */
10192         if(!this.tickable){
10193             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10194             return cfg;
10195         }
10196         
10197         /*
10198          *  ComboBox with tickable selections
10199          */
10200              
10201         var align = this.labelAlign || this.parentLabelAlign();
10202         
10203         cfg = {
10204             cls : 'form-group roo-combobox-tickable' //input-group
10205         };
10206         
10207         
10208         var buttons = {
10209             tag : 'div',
10210             cls : 'tickable-buttons',
10211             cn : [
10212                 {
10213                     tag : 'button',
10214                     type : 'button',
10215                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10216                     html : 'Edit'
10217                 },
10218                 {
10219                     tag : 'button',
10220                     type : 'button',
10221                     name : 'ok',
10222                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10223                     html : 'Done'
10224                 },
10225                 {
10226                     tag : 'button',
10227                     type : 'button',
10228                     name : 'cancel',
10229                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10230                     html : 'Cancel'
10231                 }
10232             ]
10233         };
10234         
10235         var _this = this;
10236         Roo.each(buttons.cn, function(c){
10237             if (_this.size) {
10238                 c.cls += ' btn-' + _this.size;
10239             }
10240
10241             if (_this.disabled) {
10242                 c.disabled = true;
10243             }
10244         });
10245         
10246         var box = {
10247             tag: 'div',
10248             cn: [
10249                 {
10250                     tag: 'input',
10251                     type : 'hidden',
10252                     cls: 'form-hidden-field'
10253                 },
10254                 {
10255                     tag: 'ul',
10256                     cls: 'select2-choices',
10257                     cn:[
10258                         {
10259                             tag: 'li',
10260                             cls: 'select2-search-field',
10261                             cn: [
10262
10263                                 buttons
10264                             ]
10265                         }
10266                     ]
10267                 }
10268             ]
10269         }
10270         
10271         var combobox = {
10272             cls: 'select2-container input-group select2-container-multi',
10273             cn: [
10274                 box,
10275                 {
10276                     tag: 'ul',
10277                     cls: 'typeahead typeahead-long dropdown-menu',
10278                     style: 'display:none; max-height:' + this.maxHeight + 'px;'
10279                 }
10280             ]
10281         };
10282         
10283         if (align ==='left' && this.fieldLabel.length) {
10284             
10285                 Roo.log("left and has label");
10286                 cfg.cn = [
10287                     
10288                     {
10289                         tag: 'label',
10290                         'for' :  id,
10291                         cls : 'control-label col-sm-' + this.labelWidth,
10292                         html : this.fieldLabel
10293                         
10294                     },
10295                     {
10296                         cls : "col-sm-" + (12 - this.labelWidth), 
10297                         cn: [
10298                             combobox
10299                         ]
10300                     }
10301                     
10302                 ];
10303         } else if ( this.fieldLabel.length) {
10304                 Roo.log(" label");
10305                  cfg.cn = [
10306                    
10307                     {
10308                         tag: 'label',
10309                         //cls : 'input-group-addon',
10310                         html : this.fieldLabel
10311                         
10312                     },
10313                     
10314                     combobox
10315                     
10316                 ];
10317
10318         } else {
10319             
10320                 Roo.log(" no label && no align");
10321                 cfg = combobox
10322                      
10323                 
10324         }
10325          
10326         var settings=this;
10327         ['xs','sm','md','lg'].map(function(size){
10328             if (settings[size]) {
10329                 cfg.cls += ' col-' + size + '-' + settings[size];
10330             }
10331         });
10332         
10333         return cfg;
10334         
10335     },
10336     
10337     // private
10338     initEvents: function()
10339     {
10340         
10341         if (!this.store) {
10342             throw "can not find store for combo";
10343         }
10344         this.store = Roo.factory(this.store, Roo.data);
10345         
10346         if(this.tickable){
10347             this.initTickableEvnets();
10348             return;
10349         }
10350         
10351         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10352         
10353         
10354         if(this.hiddenName){
10355             
10356             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10357             
10358             this.hiddenField.dom.value =
10359                 this.hiddenValue !== undefined ? this.hiddenValue :
10360                 this.value !== undefined ? this.value : '';
10361
10362             // prevent input submission
10363             this.el.dom.removeAttribute('name');
10364             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10365              
10366              
10367         }
10368         //if(Roo.isGecko){
10369         //    this.el.dom.setAttribute('autocomplete', 'off');
10370         //}
10371
10372         var cls = 'x-combo-list';
10373         this.list = this.el.select('ul.dropdown-menu',true).first();
10374
10375         //this.list = new Roo.Layer({
10376         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10377         //});
10378         
10379         var _this = this;
10380         
10381         (function(){
10382             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10383             _this.list.setWidth(lw);
10384         }).defer(100);
10385         
10386         this.list.on('mouseover', this.onViewOver, this);
10387         this.list.on('mousemove', this.onViewMove, this);
10388         
10389         this.list.on('scroll', this.onViewScroll, this);
10390         
10391         /*
10392         this.list.swallowEvent('mousewheel');
10393         this.assetHeight = 0;
10394
10395         if(this.title){
10396             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10397             this.assetHeight += this.header.getHeight();
10398         }
10399
10400         this.innerList = this.list.createChild({cls:cls+'-inner'});
10401         this.innerList.on('mouseover', this.onViewOver, this);
10402         this.innerList.on('mousemove', this.onViewMove, this);
10403         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10404         
10405         if(this.allowBlank && !this.pageSize && !this.disableClear){
10406             this.footer = this.list.createChild({cls:cls+'-ft'});
10407             this.pageTb = new Roo.Toolbar(this.footer);
10408            
10409         }
10410         if(this.pageSize){
10411             this.footer = this.list.createChild({cls:cls+'-ft'});
10412             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10413                     {pageSize: this.pageSize});
10414             
10415         }
10416         
10417         if (this.pageTb && this.allowBlank && !this.disableClear) {
10418             var _this = this;
10419             this.pageTb.add(new Roo.Toolbar.Fill(), {
10420                 cls: 'x-btn-icon x-btn-clear',
10421                 text: '&#160;',
10422                 handler: function()
10423                 {
10424                     _this.collapse();
10425                     _this.clearValue();
10426                     _this.onSelect(false, -1);
10427                 }
10428             });
10429         }
10430         if (this.footer) {
10431             this.assetHeight += this.footer.getHeight();
10432         }
10433         */
10434             
10435         if(!this.tpl){
10436             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10437         }
10438
10439         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10440             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10441         });
10442         //this.view.wrapEl.setDisplayed(false);
10443         this.view.on('click', this.onViewClick, this);
10444         
10445         
10446         
10447         this.store.on('beforeload', this.onBeforeLoad, this);
10448         this.store.on('load', this.onLoad, this);
10449         this.store.on('loadexception', this.onLoadException, this);
10450         /*
10451         if(this.resizable){
10452             this.resizer = new Roo.Resizable(this.list,  {
10453                pinned:true, handles:'se'
10454             });
10455             this.resizer.on('resize', function(r, w, h){
10456                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10457                 this.listWidth = w;
10458                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10459                 this.restrictHeight();
10460             }, this);
10461             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10462         }
10463         */
10464         if(!this.editable){
10465             this.editable = true;
10466             this.setEditable(false);
10467         }
10468         
10469         /*
10470         
10471         if (typeof(this.events.add.listeners) != 'undefined') {
10472             
10473             this.addicon = this.wrap.createChild(
10474                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10475        
10476             this.addicon.on('click', function(e) {
10477                 this.fireEvent('add', this);
10478             }, this);
10479         }
10480         if (typeof(this.events.edit.listeners) != 'undefined') {
10481             
10482             this.editicon = this.wrap.createChild(
10483                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10484             if (this.addicon) {
10485                 this.editicon.setStyle('margin-left', '40px');
10486             }
10487             this.editicon.on('click', function(e) {
10488                 
10489                 // we fire even  if inothing is selected..
10490                 this.fireEvent('edit', this, this.lastData );
10491                 
10492             }, this);
10493         }
10494         */
10495         
10496         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10497             "up" : function(e){
10498                 this.inKeyMode = true;
10499                 this.selectPrev();
10500             },
10501
10502             "down" : function(e){
10503                 if(!this.isExpanded()){
10504                     this.onTriggerClick();
10505                 }else{
10506                     this.inKeyMode = true;
10507                     this.selectNext();
10508                 }
10509             },
10510
10511             "enter" : function(e){
10512 //                this.onViewClick();
10513                 //return true;
10514                 this.collapse();
10515                 
10516                 if(this.fireEvent("specialkey", this, e)){
10517                     this.onViewClick(false);
10518                 }
10519                 
10520                 return true;
10521             },
10522
10523             "esc" : function(e){
10524                 this.collapse();
10525             },
10526
10527             "tab" : function(e){
10528                 this.collapse();
10529                 
10530                 if(this.fireEvent("specialkey", this, e)){
10531                     this.onViewClick(false);
10532                 }
10533                 
10534                 return true;
10535             },
10536
10537             scope : this,
10538
10539             doRelay : function(foo, bar, hname){
10540                 if(hname == 'down' || this.scope.isExpanded()){
10541                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10542                 }
10543                 return true;
10544             },
10545
10546             forceKeyDown: true
10547         });
10548         
10549         
10550         this.queryDelay = Math.max(this.queryDelay || 10,
10551                 this.mode == 'local' ? 10 : 250);
10552         
10553         
10554         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10555         
10556         if(this.typeAhead){
10557             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10558         }
10559         if(this.editable !== false){
10560             this.inputEl().on("keyup", this.onKeyUp, this);
10561         }
10562         if(this.forceSelection){
10563             this.inputEl().on('blur', this.doForce, this);
10564         }
10565         
10566         if(this.multiple){
10567             this.choices = this.el.select('ul.select2-choices', true).first();
10568             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10569         }
10570     },
10571     
10572     initTickableEvnets: function()
10573     {   
10574         if(this.hiddenName){
10575             
10576             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10577             
10578             this.hiddenField.dom.value =
10579                 this.hiddenValue !== undefined ? this.hiddenValue :
10580                 this.value !== undefined ? this.value : '';
10581
10582             // prevent input submission
10583             this.el.dom.removeAttribute('name');
10584             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10585              
10586              
10587         }
10588         
10589         this.list = this.el.select('ul.dropdown-menu',true).first();
10590         
10591         this.choices = this.el.select('ul.select2-choices', true).first();
10592         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10593         this.searchField.on("click", this.onTriggerClick, this, {preventDefault:true});
10594          
10595         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10596         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10597         
10598         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10599         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10600         
10601         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10602         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10603         
10604         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10605         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10606         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10607         
10608         this.okBtn.hide();
10609         this.cancelBtn.hide();
10610         
10611         var _this = this;
10612         
10613         (function(){
10614             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10615             _this.list.setWidth(lw);
10616         }).defer(100);
10617         
10618         this.list.on('mouseover', this.onViewOver, this);
10619         this.list.on('mousemove', this.onViewMove, this);
10620         
10621         this.list.on('scroll', this.onViewScroll, this);
10622         
10623         if(!this.tpl){
10624             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
10625         }
10626
10627         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10628             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10629         });
10630         
10631         //this.view.wrapEl.setDisplayed(false);
10632         this.view.on('click', this.onViewClick, this);
10633         
10634         
10635         
10636         this.store.on('beforeload', this.onBeforeLoad, this);
10637         this.store.on('load', this.onLoad, this);
10638         this.store.on('loadexception', this.onLoadException, this);
10639         
10640 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10641 //            "up" : function(e){
10642 //                this.inKeyMode = true;
10643 //                this.selectPrev();
10644 //            },
10645 //
10646 //            "down" : function(e){
10647 //                if(!this.isExpanded()){
10648 //                    this.onTriggerClick();
10649 //                }else{
10650 //                    this.inKeyMode = true;
10651 //                    this.selectNext();
10652 //                }
10653 //            },
10654 //
10655 //            "enter" : function(e){
10656 ////                this.onViewClick();
10657 //                //return true;
10658 //                this.collapse();
10659 //                
10660 //                if(this.fireEvent("specialkey", this, e)){
10661 //                    this.onViewClick(false);
10662 //                }
10663 //                
10664 //                return true;
10665 //            },
10666 //
10667 //            "esc" : function(e){
10668 //                this.collapse();
10669 //            },
10670 //
10671 //            "tab" : function(e){
10672 //                this.collapse();
10673 //                
10674 //                if(this.fireEvent("specialkey", this, e)){
10675 //                    this.onViewClick(false);
10676 //                }
10677 //                
10678 //                return true;
10679 //            },
10680 //
10681 //            scope : this,
10682 //
10683 //            doRelay : function(foo, bar, hname){
10684 //                if(hname == 'down' || this.scope.isExpanded()){
10685 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10686 //                }
10687 //                return true;
10688 //            },
10689 //
10690 //            forceKeyDown: true
10691 //        });
10692         
10693         
10694         this.queryDelay = Math.max(this.queryDelay || 10,
10695                 this.mode == 'local' ? 10 : 250);
10696         
10697         
10698         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10699         
10700         if(this.typeAhead){
10701             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10702         }
10703     },
10704
10705     onDestroy : function(){
10706         if(this.view){
10707             this.view.setStore(null);
10708             this.view.el.removeAllListeners();
10709             this.view.el.remove();
10710             this.view.purgeListeners();
10711         }
10712         if(this.list){
10713             this.list.dom.innerHTML  = '';
10714         }
10715         
10716         if(this.store){
10717             this.store.un('beforeload', this.onBeforeLoad, this);
10718             this.store.un('load', this.onLoad, this);
10719             this.store.un('loadexception', this.onLoadException, this);
10720         }
10721         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10722     },
10723
10724     // private
10725     fireKey : function(e){
10726         if(e.isNavKeyPress() && !this.list.isVisible()){
10727             this.fireEvent("specialkey", this, e);
10728         }
10729     },
10730
10731     // private
10732     onResize: function(w, h){
10733 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10734 //        
10735 //        if(typeof w != 'number'){
10736 //            // we do not handle it!?!?
10737 //            return;
10738 //        }
10739 //        var tw = this.trigger.getWidth();
10740 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10741 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10742 //        var x = w - tw;
10743 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10744 //            
10745 //        //this.trigger.setStyle('left', x+'px');
10746 //        
10747 //        if(this.list && this.listWidth === undefined){
10748 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10749 //            this.list.setWidth(lw);
10750 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10751 //        }
10752         
10753     
10754         
10755     },
10756
10757     /**
10758      * Allow or prevent the user from directly editing the field text.  If false is passed,
10759      * the user will only be able to select from the items defined in the dropdown list.  This method
10760      * is the runtime equivalent of setting the 'editable' config option at config time.
10761      * @param {Boolean} value True to allow the user to directly edit the field text
10762      */
10763     setEditable : function(value){
10764         if(value == this.editable){
10765             return;
10766         }
10767         this.editable = value;
10768         if(!value){
10769             this.inputEl().dom.setAttribute('readOnly', true);
10770             this.inputEl().on('mousedown', this.onTriggerClick,  this);
10771             this.inputEl().addClass('x-combo-noedit');
10772         }else{
10773             this.inputEl().dom.setAttribute('readOnly', false);
10774             this.inputEl().un('mousedown', this.onTriggerClick,  this);
10775             this.inputEl().removeClass('x-combo-noedit');
10776         }
10777     },
10778
10779     // private
10780     
10781     onBeforeLoad : function(combo,opts){
10782         if(!this.hasFocus){
10783             return;
10784         }
10785          if (!opts.add) {
10786             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
10787          }
10788         this.restrictHeight();
10789         this.selectedIndex = -1;
10790     },
10791
10792     // private
10793     onLoad : function(){
10794         
10795         this.hasQuery = false;
10796         
10797         if(!this.hasFocus){
10798             return;
10799         }
10800         
10801         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10802             this.loading.hide();
10803         }
10804         
10805         if(this.store.getCount() > 0){
10806             this.expand();
10807             this.restrictHeight();
10808             if(this.lastQuery == this.allQuery){
10809                 if(this.editable && !this.tickable){
10810                     this.inputEl().dom.select();
10811                 }
10812                 if(!this.selectByValue(this.value, true) && this.autoFocus){
10813                     this.select(0, true);
10814                 }
10815             }else{
10816                 if(this.autoFocus){
10817                     this.selectNext();
10818                 }
10819                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
10820                     this.taTask.delay(this.typeAheadDelay);
10821                 }
10822             }
10823         }else{
10824             this.onEmptyResults();
10825         }
10826         
10827         //this.el.focus();
10828     },
10829     // private
10830     onLoadException : function()
10831     {
10832         this.hasQuery = false;
10833         
10834         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10835             this.loading.hide();
10836         }
10837         
10838         this.collapse();
10839         Roo.log(this.store.reader.jsonData);
10840         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
10841             // fixme
10842             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
10843         }
10844         
10845         
10846     },
10847     // private
10848     onTypeAhead : function(){
10849         if(this.store.getCount() > 0){
10850             var r = this.store.getAt(0);
10851             var newValue = r.data[this.displayField];
10852             var len = newValue.length;
10853             var selStart = this.getRawValue().length;
10854             
10855             if(selStart != len){
10856                 this.setRawValue(newValue);
10857                 this.selectText(selStart, newValue.length);
10858             }
10859         }
10860     },
10861
10862     // private
10863     onSelect : function(record, index){
10864         
10865         if(this.fireEvent('beforeselect', this, record, index) !== false){
10866         
10867             this.setFromData(index > -1 ? record.data : false);
10868             
10869             this.collapse();
10870             this.fireEvent('select', this, record, index);
10871         }
10872     },
10873
10874     /**
10875      * Returns the currently selected field value or empty string if no value is set.
10876      * @return {String} value The selected value
10877      */
10878     getValue : function(){
10879         
10880         if(this.multiple){
10881             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
10882         }
10883         
10884         if(this.valueField){
10885             return typeof this.value != 'undefined' ? this.value : '';
10886         }else{
10887             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
10888         }
10889     },
10890
10891     /**
10892      * Clears any text/value currently set in the field
10893      */
10894     clearValue : function(){
10895         if(this.hiddenField){
10896             this.hiddenField.dom.value = '';
10897         }
10898         this.value = '';
10899         this.setRawValue('');
10900         this.lastSelectionText = '';
10901         
10902     },
10903
10904     /**
10905      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
10906      * will be displayed in the field.  If the value does not match the data value of an existing item,
10907      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
10908      * Otherwise the field will be blank (although the value will still be set).
10909      * @param {String} value The value to match
10910      */
10911     setValue : function(v){
10912         if(this.multiple){
10913             this.syncValue();
10914             return;
10915         }
10916         
10917         var text = v;
10918         if(this.valueField){
10919             var r = this.findRecord(this.valueField, v);
10920             if(r){
10921                 text = r.data[this.displayField];
10922             }else if(this.valueNotFoundText !== undefined){
10923                 text = this.valueNotFoundText;
10924             }
10925         }
10926         this.lastSelectionText = text;
10927         if(this.hiddenField){
10928             this.hiddenField.dom.value = v;
10929         }
10930         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
10931         this.value = v;
10932     },
10933     /**
10934      * @property {Object} the last set data for the element
10935      */
10936     
10937     lastData : false,
10938     /**
10939      * Sets the value of the field based on a object which is related to the record format for the store.
10940      * @param {Object} value the value to set as. or false on reset?
10941      */
10942     setFromData : function(o){
10943         
10944         if(this.multiple){
10945             this.addItem(o);
10946             return;
10947         }
10948             
10949         var dv = ''; // display value
10950         var vv = ''; // value value..
10951         this.lastData = o;
10952         if (this.displayField) {
10953             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10954         } else {
10955             // this is an error condition!!!
10956             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10957         }
10958         
10959         if(this.valueField){
10960             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
10961         }
10962         
10963         if(this.hiddenField){
10964             this.hiddenField.dom.value = vv;
10965             
10966             this.lastSelectionText = dv;
10967             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
10968             this.value = vv;
10969             return;
10970         }
10971         // no hidden field.. - we store the value in 'value', but still display
10972         // display field!!!!
10973         this.lastSelectionText = dv;
10974         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
10975         this.value = vv;
10976         
10977         
10978     },
10979     // private
10980     reset : function(){
10981         // overridden so that last data is reset..
10982         this.setValue(this.originalValue);
10983         this.clearInvalid();
10984         this.lastData = false;
10985         if (this.view) {
10986             this.view.clearSelections();
10987         }
10988     },
10989     // private
10990     findRecord : function(prop, value){
10991         var record;
10992         if(this.store.getCount() > 0){
10993             this.store.each(function(r){
10994                 if(r.data[prop] == value){
10995                     record = r;
10996                     return false;
10997                 }
10998                 return true;
10999             });
11000         }
11001         return record;
11002     },
11003     
11004     getName: function()
11005     {
11006         // returns hidden if it's set..
11007         if (!this.rendered) {return ''};
11008         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11009         
11010     },
11011     // private
11012     onViewMove : function(e, t){
11013         this.inKeyMode = false;
11014     },
11015
11016     // private
11017     onViewOver : function(e, t){
11018         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11019             return;
11020         }
11021         var item = this.view.findItemFromChild(t);
11022         
11023         if(item){
11024             var index = this.view.indexOf(item);
11025             this.select(index, false);
11026         }
11027     },
11028
11029     // private
11030     onViewClick : function(view, doFocus, el, e)
11031     {
11032         var index = this.view.getSelectedIndexes()[0];
11033         
11034         var r = this.store.getAt(index);
11035         
11036         if(this.tickable){
11037             
11038             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11039                 return;
11040             }
11041             
11042             var rm = false;
11043             var _this = this;
11044             
11045             Roo.each(this.tickItems, function(v,k){
11046                 
11047                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11048                     _this.tickItems.splice(k, 1);
11049                     rm = true;
11050                     return;
11051                 }
11052             })
11053             
11054             if(rm){
11055                 return;
11056             }
11057             
11058             this.tickItems.push(r.data);
11059             return;
11060         }
11061         
11062         if(r){
11063             this.onSelect(r, index);
11064         }
11065         if(doFocus !== false && !this.blockFocus){
11066             this.inputEl().focus();
11067         }
11068     },
11069
11070     // private
11071     restrictHeight : function(){
11072         //this.innerList.dom.style.height = '';
11073         //var inner = this.innerList.dom;
11074         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11075         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11076         //this.list.beginUpdate();
11077         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11078         this.list.alignTo(this.inputEl(), this.listAlign);
11079         //this.list.endUpdate();
11080     },
11081
11082     // private
11083     onEmptyResults : function(){
11084         this.collapse();
11085     },
11086
11087     /**
11088      * Returns true if the dropdown list is expanded, else false.
11089      */
11090     isExpanded : function(){
11091         return this.list.isVisible();
11092     },
11093
11094     /**
11095      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11096      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11097      * @param {String} value The data value of the item to select
11098      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11099      * selected item if it is not currently in view (defaults to true)
11100      * @return {Boolean} True if the value matched an item in the list, else false
11101      */
11102     selectByValue : function(v, scrollIntoView){
11103         if(v !== undefined && v !== null){
11104             var r = this.findRecord(this.valueField || this.displayField, v);
11105             if(r){
11106                 this.select(this.store.indexOf(r), scrollIntoView);
11107                 return true;
11108             }
11109         }
11110         return false;
11111     },
11112
11113     /**
11114      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11115      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11116      * @param {Number} index The zero-based index of the list item to select
11117      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11118      * selected item if it is not currently in view (defaults to true)
11119      */
11120     select : function(index, scrollIntoView){
11121         this.selectedIndex = index;
11122         this.view.select(index);
11123         if(scrollIntoView !== false){
11124             var el = this.view.getNode(index);
11125             if(el){
11126                 //this.innerList.scrollChildIntoView(el, false);
11127                 
11128             }
11129         }
11130     },
11131
11132     // private
11133     selectNext : function(){
11134         var ct = this.store.getCount();
11135         if(ct > 0){
11136             if(this.selectedIndex == -1){
11137                 this.select(0);
11138             }else if(this.selectedIndex < ct-1){
11139                 this.select(this.selectedIndex+1);
11140             }
11141         }
11142     },
11143
11144     // private
11145     selectPrev : function(){
11146         var ct = this.store.getCount();
11147         if(ct > 0){
11148             if(this.selectedIndex == -1){
11149                 this.select(0);
11150             }else if(this.selectedIndex != 0){
11151                 this.select(this.selectedIndex-1);
11152             }
11153         }
11154     },
11155
11156     // private
11157     onKeyUp : function(e){
11158         if(this.editable !== false && !e.isSpecialKey()){
11159             this.lastKey = e.getKey();
11160             this.dqTask.delay(this.queryDelay);
11161         }
11162     },
11163
11164     // private
11165     validateBlur : function(){
11166         return !this.list || !this.list.isVisible();   
11167     },
11168
11169     // private
11170     initQuery : function(){
11171         this.doQuery(this.getRawValue());
11172     },
11173
11174     // private
11175     doForce : function(){
11176         if(this.inputEl().dom.value.length > 0){
11177             this.inputEl().dom.value =
11178                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11179              
11180         }
11181     },
11182
11183     /**
11184      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11185      * query allowing the query action to be canceled if needed.
11186      * @param {String} query The SQL query to execute
11187      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11188      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11189      * saved in the current store (defaults to false)
11190      */
11191     doQuery : function(q, forceAll){
11192         
11193         if(q === undefined || q === null){
11194             q = '';
11195         }
11196         var qe = {
11197             query: q,
11198             forceAll: forceAll,
11199             combo: this,
11200             cancel:false
11201         };
11202         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11203             return false;
11204         }
11205         q = qe.query;
11206         
11207         forceAll = qe.forceAll;
11208         if(forceAll === true || (q.length >= this.minChars)){
11209             
11210             this.hasQuery = true;
11211             
11212             if(this.lastQuery != q || this.alwaysQuery){
11213                 this.lastQuery = q;
11214                 if(this.mode == 'local'){
11215                     this.selectedIndex = -1;
11216                     if(forceAll){
11217                         this.store.clearFilter();
11218                     }else{
11219                         this.store.filter(this.displayField, q);
11220                     }
11221                     this.onLoad();
11222                 }else{
11223                     this.store.baseParams[this.queryParam] = q;
11224                     
11225                     var options = {params : this.getParams(q)};
11226                     
11227                     if(this.loadNext){
11228                         options.add = true;
11229                         options.params.start = this.page * this.pageSize;
11230                     }
11231                     
11232                     this.store.load(options);
11233                     /*
11234                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11235                      *  we should expand the list on onLoad
11236                      *  so command out it
11237                      */
11238 //                    this.expand();
11239                 }
11240             }else{
11241                 this.selectedIndex = -1;
11242                 this.onLoad();   
11243             }
11244         }
11245         
11246         this.loadNext = false;
11247     },
11248
11249     // private
11250     getParams : function(q){
11251         var p = {};
11252         //p[this.queryParam] = q;
11253         
11254         if(this.pageSize){
11255             p.start = 0;
11256             p.limit = this.pageSize;
11257         }
11258         return p;
11259     },
11260
11261     /**
11262      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11263      */
11264     collapse : function(){
11265         if(!this.isExpanded()){
11266             return;
11267         }
11268         
11269         this.list.hide();
11270         
11271         if(this.tickable){
11272             this.okBtn.hide();
11273             this.cancelBtn.hide();
11274             this.trigger.show();
11275         }
11276         
11277         Roo.get(document).un('mousedown', this.collapseIf, this);
11278         Roo.get(document).un('mousewheel', this.collapseIf, this);
11279         if (!this.editable) {
11280             Roo.get(document).un('keydown', this.listKeyPress, this);
11281         }
11282         this.fireEvent('collapse', this);
11283     },
11284
11285     // private
11286     collapseIf : function(e){
11287         var in_combo  = e.within(this.el);
11288         var in_list =  e.within(this.list);
11289         
11290         if (in_combo || in_list) {
11291             //e.stopPropagation();
11292             return;
11293         }
11294         
11295         if(this.tickable){
11296             this.onTickableFooterButtonClick(e, false, false);
11297         }
11298
11299         this.collapse();
11300         
11301     },
11302
11303     /**
11304      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11305      */
11306     expand : function(){
11307        
11308         if(this.isExpanded() || !this.hasFocus){
11309             return;
11310         }
11311          Roo.log('expand');
11312         this.list.alignTo(this.inputEl(), this.listAlign);
11313         this.list.show();
11314         
11315         if(this.tickable){
11316             
11317             this.tickItems = Roo.apply([], this.item);
11318             
11319             this.okBtn.show();
11320             this.cancelBtn.show();
11321             this.trigger.hide();
11322             
11323         }
11324         
11325         Roo.get(document).on('mousedown', this.collapseIf, this);
11326         Roo.get(document).on('mousewheel', this.collapseIf, this);
11327         if (!this.editable) {
11328             Roo.get(document).on('keydown', this.listKeyPress, this);
11329         }
11330         
11331         this.fireEvent('expand', this);
11332     },
11333
11334     // private
11335     // Implements the default empty TriggerField.onTriggerClick function
11336     onTriggerClick : function()
11337     {
11338         Roo.log('trigger click');
11339         
11340         if(this.disabled){
11341             return;
11342         }
11343         
11344         if(this.tickable){
11345             this.onTickableTriggerClick();
11346             return;
11347         }
11348         
11349         this.page = 0;
11350         this.loadNext = false;
11351         
11352         if(this.isExpanded()){
11353             this.collapse();
11354             if (!this.blockFocus) {
11355                 this.inputEl().focus();
11356             }
11357             
11358         }else {
11359             this.hasFocus = true;
11360             if(this.triggerAction == 'all') {
11361                 this.doQuery(this.allQuery, true);
11362             } else {
11363                 this.doQuery(this.getRawValue());
11364             }
11365             if (!this.blockFocus) {
11366                 this.inputEl().focus();
11367             }
11368         }
11369     },
11370     
11371     onTickableTriggerClick : function()
11372     {
11373         this.page = 0;
11374         this.loadNext = false;
11375         this.hasFocus = true;
11376         
11377         if(this.triggerAction == 'all') {
11378             this.doQuery(this.allQuery, true);
11379         } else {
11380             this.doQuery(this.getRawValue());
11381         }
11382     },
11383     
11384     listKeyPress : function(e)
11385     {
11386         //Roo.log('listkeypress');
11387         // scroll to first matching element based on key pres..
11388         if (e.isSpecialKey()) {
11389             return false;
11390         }
11391         var k = String.fromCharCode(e.getKey()).toUpperCase();
11392         //Roo.log(k);
11393         var match  = false;
11394         var csel = this.view.getSelectedNodes();
11395         var cselitem = false;
11396         if (csel.length) {
11397             var ix = this.view.indexOf(csel[0]);
11398             cselitem  = this.store.getAt(ix);
11399             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11400                 cselitem = false;
11401             }
11402             
11403         }
11404         
11405         this.store.each(function(v) { 
11406             if (cselitem) {
11407                 // start at existing selection.
11408                 if (cselitem.id == v.id) {
11409                     cselitem = false;
11410                 }
11411                 return true;
11412             }
11413                 
11414             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11415                 match = this.store.indexOf(v);
11416                 return false;
11417             }
11418             return true;
11419         }, this);
11420         
11421         if (match === false) {
11422             return true; // no more action?
11423         }
11424         // scroll to?
11425         this.view.select(match);
11426         var sn = Roo.get(this.view.getSelectedNodes()[0])
11427         //sn.scrollIntoView(sn.dom.parentNode, false);
11428     },
11429     
11430     onViewScroll : function(e, t){
11431         
11432         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11433             return;
11434         }
11435         
11436         this.hasQuery = true;
11437         
11438         this.loading = this.list.select('.loading', true).first();
11439         
11440         if(this.loading === null){
11441             this.list.createChild({
11442                 tag: 'div',
11443                 cls: 'loading select2-more-results select2-active',
11444                 html: 'Loading more results...'
11445             })
11446             
11447             this.loading = this.list.select('.loading', true).first();
11448             
11449             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11450             
11451             this.loading.hide();
11452         }
11453         
11454         this.loading.show();
11455         
11456         var _combo = this;
11457         
11458         this.page++;
11459         this.loadNext = true;
11460         
11461         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11462         
11463         return;
11464     },
11465     
11466     addItem : function(o)
11467     {   
11468         var dv = ''; // display value
11469         
11470         if (this.displayField) {
11471             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11472         } else {
11473             // this is an error condition!!!
11474             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11475         }
11476         
11477         if(!dv.length){
11478             return;
11479         }
11480         
11481         var choice = this.choices.createChild({
11482             tag: 'li',
11483             cls: 'select2-search-choice',
11484             cn: [
11485                 {
11486                     tag: 'div',
11487                     html: dv
11488                 },
11489                 {
11490                     tag: 'a',
11491                     href: '#',
11492                     cls: 'select2-search-choice-close',
11493                     tabindex: '-1'
11494                 }
11495             ]
11496             
11497         }, this.searchField);
11498         
11499         var close = choice.select('a.select2-search-choice-close', true).first()
11500         
11501         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11502         
11503         this.item.push(o);
11504         
11505         this.lastData = o;
11506         
11507         this.syncValue();
11508         
11509         this.inputEl().dom.value = '';
11510         
11511     },
11512     
11513     onRemoveItem : function(e, _self, o)
11514     {
11515         e.preventDefault();
11516         var index = this.item.indexOf(o.data) * 1;
11517         
11518         if( index < 0){
11519             Roo.log('not this item?!');
11520             return;
11521         }
11522         
11523         this.item.splice(index, 1);
11524         o.item.remove();
11525         
11526         this.syncValue();
11527         
11528         this.fireEvent('remove', this, e);
11529         
11530     },
11531     
11532     syncValue : function()
11533     {
11534         if(!this.item.length){
11535             this.clearValue();
11536             return;
11537         }
11538             
11539         var value = [];
11540         var _this = this;
11541         Roo.each(this.item, function(i){
11542             if(_this.valueField){
11543                 value.push(i[_this.valueField]);
11544                 return;
11545             }
11546
11547             value.push(i);
11548         });
11549
11550         this.value = value.join(',');
11551
11552         if(this.hiddenField){
11553             this.hiddenField.dom.value = this.value;
11554         }
11555     },
11556     
11557     clearItem : function()
11558     {
11559         if(!this.multiple){
11560             return;
11561         }
11562         
11563         this.item = [];
11564         
11565         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11566            c.remove();
11567         });
11568         
11569         this.syncValue();
11570     },
11571     
11572     inputEl: function ()
11573     {
11574         if(this.tickable){
11575             return this.searchField;
11576         }
11577         return this.el.select('input.form-control',true).first();
11578     },
11579     
11580     
11581     onTickableFooterButtonClick : function(e, btn, el)
11582     {
11583         e.preventDefault();
11584         
11585         if(btn && btn.name == 'cancel'){
11586             this.tickItems = Roo.apply([], this.item);
11587             this.collapse();
11588             return;
11589         }
11590         
11591         this.clearItem();
11592         
11593         var _this = this;
11594         
11595         Roo.each(this.tickItems, function(o){
11596             _this.addItem(o);
11597         });
11598         
11599         this.collapse();
11600         
11601     }
11602     
11603     
11604
11605     /** 
11606     * @cfg {Boolean} grow 
11607     * @hide 
11608     */
11609     /** 
11610     * @cfg {Number} growMin 
11611     * @hide 
11612     */
11613     /** 
11614     * @cfg {Number} growMax 
11615     * @hide 
11616     */
11617     /**
11618      * @hide
11619      * @method autoSize
11620      */
11621 });
11622 /*
11623  * Based on:
11624  * Ext JS Library 1.1.1
11625  * Copyright(c) 2006-2007, Ext JS, LLC.
11626  *
11627  * Originally Released Under LGPL - original licence link has changed is not relivant.
11628  *
11629  * Fork - LGPL
11630  * <script type="text/javascript">
11631  */
11632
11633 /**
11634  * @class Roo.View
11635  * @extends Roo.util.Observable
11636  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11637  * This class also supports single and multi selection modes. <br>
11638  * Create a data model bound view:
11639  <pre><code>
11640  var store = new Roo.data.Store(...);
11641
11642  var view = new Roo.View({
11643     el : "my-element",
11644     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11645  
11646     singleSelect: true,
11647     selectedClass: "ydataview-selected",
11648     store: store
11649  });
11650
11651  // listen for node click?
11652  view.on("click", function(vw, index, node, e){
11653  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11654  });
11655
11656  // load XML data
11657  dataModel.load("foobar.xml");
11658  </code></pre>
11659  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11660  * <br><br>
11661  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11662  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11663  * 
11664  * Note: old style constructor is still suported (container, template, config)
11665  * 
11666  * @constructor
11667  * Create a new View
11668  * @param {Object} config The config object
11669  * 
11670  */
11671 Roo.View = function(config, depreciated_tpl, depreciated_config){
11672     
11673     this.parent = false;
11674     
11675     if (typeof(depreciated_tpl) == 'undefined') {
11676         // new way.. - universal constructor.
11677         Roo.apply(this, config);
11678         this.el  = Roo.get(this.el);
11679     } else {
11680         // old format..
11681         this.el  = Roo.get(config);
11682         this.tpl = depreciated_tpl;
11683         Roo.apply(this, depreciated_config);
11684     }
11685     this.wrapEl  = this.el.wrap().wrap();
11686     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11687     
11688     
11689     if(typeof(this.tpl) == "string"){
11690         this.tpl = new Roo.Template(this.tpl);
11691     } else {
11692         // support xtype ctors..
11693         this.tpl = new Roo.factory(this.tpl, Roo);
11694     }
11695     
11696     
11697     this.tpl.compile();
11698     
11699     /** @private */
11700     this.addEvents({
11701         /**
11702          * @event beforeclick
11703          * Fires before a click is processed. Returns false to cancel the default action.
11704          * @param {Roo.View} this
11705          * @param {Number} index The index of the target node
11706          * @param {HTMLElement} node The target node
11707          * @param {Roo.EventObject} e The raw event object
11708          */
11709             "beforeclick" : true,
11710         /**
11711          * @event click
11712          * Fires when a template node is clicked.
11713          * @param {Roo.View} this
11714          * @param {Number} index The index of the target node
11715          * @param {HTMLElement} node The target node
11716          * @param {Roo.EventObject} e The raw event object
11717          */
11718             "click" : true,
11719         /**
11720          * @event dblclick
11721          * Fires when a template node is double clicked.
11722          * @param {Roo.View} this
11723          * @param {Number} index The index of the target node
11724          * @param {HTMLElement} node The target node
11725          * @param {Roo.EventObject} e The raw event object
11726          */
11727             "dblclick" : true,
11728         /**
11729          * @event contextmenu
11730          * Fires when a template node is right clicked.
11731          * @param {Roo.View} this
11732          * @param {Number} index The index of the target node
11733          * @param {HTMLElement} node The target node
11734          * @param {Roo.EventObject} e The raw event object
11735          */
11736             "contextmenu" : true,
11737         /**
11738          * @event selectionchange
11739          * Fires when the selected nodes change.
11740          * @param {Roo.View} this
11741          * @param {Array} selections Array of the selected nodes
11742          */
11743             "selectionchange" : true,
11744     
11745         /**
11746          * @event beforeselect
11747          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
11748          * @param {Roo.View} this
11749          * @param {HTMLElement} node The node to be selected
11750          * @param {Array} selections Array of currently selected nodes
11751          */
11752             "beforeselect" : true,
11753         /**
11754          * @event preparedata
11755          * Fires on every row to render, to allow you to change the data.
11756          * @param {Roo.View} this
11757          * @param {Object} data to be rendered (change this)
11758          */
11759           "preparedata" : true
11760           
11761           
11762         });
11763
11764
11765
11766     this.el.on({
11767         "click": this.onClick,
11768         "dblclick": this.onDblClick,
11769         "contextmenu": this.onContextMenu,
11770         scope:this
11771     });
11772
11773     this.selections = [];
11774     this.nodes = [];
11775     this.cmp = new Roo.CompositeElementLite([]);
11776     if(this.store){
11777         this.store = Roo.factory(this.store, Roo.data);
11778         this.setStore(this.store, true);
11779     }
11780     
11781     if ( this.footer && this.footer.xtype) {
11782            
11783          var fctr = this.wrapEl.appendChild(document.createElement("div"));
11784         
11785         this.footer.dataSource = this.store
11786         this.footer.container = fctr;
11787         this.footer = Roo.factory(this.footer, Roo);
11788         fctr.insertFirst(this.el);
11789         
11790         // this is a bit insane - as the paging toolbar seems to detach the el..
11791 //        dom.parentNode.parentNode.parentNode
11792          // they get detached?
11793     }
11794     
11795     
11796     Roo.View.superclass.constructor.call(this);
11797     
11798     
11799 };
11800
11801 Roo.extend(Roo.View, Roo.util.Observable, {
11802     
11803      /**
11804      * @cfg {Roo.data.Store} store Data store to load data from.
11805      */
11806     store : false,
11807     
11808     /**
11809      * @cfg {String|Roo.Element} el The container element.
11810      */
11811     el : '',
11812     
11813     /**
11814      * @cfg {String|Roo.Template} tpl The template used by this View 
11815      */
11816     tpl : false,
11817     /**
11818      * @cfg {String} dataName the named area of the template to use as the data area
11819      *                          Works with domtemplates roo-name="name"
11820      */
11821     dataName: false,
11822     /**
11823      * @cfg {String} selectedClass The css class to add to selected nodes
11824      */
11825     selectedClass : "x-view-selected",
11826      /**
11827      * @cfg {String} emptyText The empty text to show when nothing is loaded.
11828      */
11829     emptyText : "",
11830     
11831     /**
11832      * @cfg {String} text to display on mask (default Loading)
11833      */
11834     mask : false,
11835     /**
11836      * @cfg {Boolean} multiSelect Allow multiple selection
11837      */
11838     multiSelect : false,
11839     /**
11840      * @cfg {Boolean} singleSelect Allow single selection
11841      */
11842     singleSelect:  false,
11843     
11844     /**
11845      * @cfg {Boolean} toggleSelect - selecting 
11846      */
11847     toggleSelect : false,
11848     
11849     /**
11850      * @cfg {Boolean} tickable - selecting 
11851      */
11852     tickable : false,
11853     
11854     /**
11855      * Returns the element this view is bound to.
11856      * @return {Roo.Element}
11857      */
11858     getEl : function(){
11859         return this.wrapEl;
11860     },
11861     
11862     
11863
11864     /**
11865      * Refreshes the view. - called by datachanged on the store. - do not call directly.
11866      */
11867     refresh : function(){
11868         Roo.log('refresh');
11869         var t = this.tpl;
11870         
11871         // if we are using something like 'domtemplate', then
11872         // the what gets used is:
11873         // t.applySubtemplate(NAME, data, wrapping data..)
11874         // the outer template then get' applied with
11875         //     the store 'extra data'
11876         // and the body get's added to the
11877         //      roo-name="data" node?
11878         //      <span class='roo-tpl-{name}'></span> ?????
11879         
11880         
11881         
11882         this.clearSelections();
11883         this.el.update("");
11884         var html = [];
11885         var records = this.store.getRange();
11886         if(records.length < 1) {
11887             
11888             // is this valid??  = should it render a template??
11889             
11890             this.el.update(this.emptyText);
11891             return;
11892         }
11893         var el = this.el;
11894         if (this.dataName) {
11895             this.el.update(t.apply(this.store.meta)); //????
11896             el = this.el.child('.roo-tpl-' + this.dataName);
11897         }
11898         
11899         for(var i = 0, len = records.length; i < len; i++){
11900             var data = this.prepareData(records[i].data, i, records[i]);
11901             this.fireEvent("preparedata", this, data, i, records[i]);
11902             
11903             var d = Roo.apply({}, data);
11904             
11905             if(this.tickable){
11906                 Roo.apply(d, {'roo-id' : Roo.id()});
11907                 
11908                 var _this = this;
11909             
11910                 Roo.each(this.parent.item, function(item){
11911                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
11912                         return;
11913                     }
11914                     Roo.apply(d, {'roo-data-checked' : 'checked'});
11915                 });
11916             }
11917             
11918             html[html.length] = Roo.util.Format.trim(
11919                 this.dataName ?
11920                     t.applySubtemplate(this.dataName, d, this.store.meta) :
11921                     t.apply(d)
11922             );
11923         }
11924         
11925         
11926         
11927         el.update(html.join(""));
11928         this.nodes = el.dom.childNodes;
11929         this.updateIndexes(0);
11930     },
11931     
11932
11933     /**
11934      * Function to override to reformat the data that is sent to
11935      * the template for each node.
11936      * DEPRICATED - use the preparedata event handler.
11937      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
11938      * a JSON object for an UpdateManager bound view).
11939      */
11940     prepareData : function(data, index, record)
11941     {
11942         this.fireEvent("preparedata", this, data, index, record);
11943         return data;
11944     },
11945
11946     onUpdate : function(ds, record){
11947          Roo.log('on update');   
11948         this.clearSelections();
11949         var index = this.store.indexOf(record);
11950         var n = this.nodes[index];
11951         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
11952         n.parentNode.removeChild(n);
11953         this.updateIndexes(index, index);
11954     },
11955
11956     
11957     
11958 // --------- FIXME     
11959     onAdd : function(ds, records, index)
11960     {
11961         Roo.log(['on Add', ds, records, index] );        
11962         this.clearSelections();
11963         if(this.nodes.length == 0){
11964             this.refresh();
11965             return;
11966         }
11967         var n = this.nodes[index];
11968         for(var i = 0, len = records.length; i < len; i++){
11969             var d = this.prepareData(records[i].data, i, records[i]);
11970             if(n){
11971                 this.tpl.insertBefore(n, d);
11972             }else{
11973                 
11974                 this.tpl.append(this.el, d);
11975             }
11976         }
11977         this.updateIndexes(index);
11978     },
11979
11980     onRemove : function(ds, record, index){
11981         Roo.log('onRemove');
11982         this.clearSelections();
11983         var el = this.dataName  ?
11984             this.el.child('.roo-tpl-' + this.dataName) :
11985             this.el; 
11986         
11987         el.dom.removeChild(this.nodes[index]);
11988         this.updateIndexes(index);
11989     },
11990
11991     /**
11992      * Refresh an individual node.
11993      * @param {Number} index
11994      */
11995     refreshNode : function(index){
11996         this.onUpdate(this.store, this.store.getAt(index));
11997     },
11998
11999     updateIndexes : function(startIndex, endIndex){
12000         var ns = this.nodes;
12001         startIndex = startIndex || 0;
12002         endIndex = endIndex || ns.length - 1;
12003         for(var i = startIndex; i <= endIndex; i++){
12004             ns[i].nodeIndex = i;
12005         }
12006     },
12007
12008     /**
12009      * Changes the data store this view uses and refresh the view.
12010      * @param {Store} store
12011      */
12012     setStore : function(store, initial){
12013         if(!initial && this.store){
12014             this.store.un("datachanged", this.refresh);
12015             this.store.un("add", this.onAdd);
12016             this.store.un("remove", this.onRemove);
12017             this.store.un("update", this.onUpdate);
12018             this.store.un("clear", this.refresh);
12019             this.store.un("beforeload", this.onBeforeLoad);
12020             this.store.un("load", this.onLoad);
12021             this.store.un("loadexception", this.onLoad);
12022         }
12023         if(store){
12024           
12025             store.on("datachanged", this.refresh, this);
12026             store.on("add", this.onAdd, this);
12027             store.on("remove", this.onRemove, this);
12028             store.on("update", this.onUpdate, this);
12029             store.on("clear", this.refresh, this);
12030             store.on("beforeload", this.onBeforeLoad, this);
12031             store.on("load", this.onLoad, this);
12032             store.on("loadexception", this.onLoad, this);
12033         }
12034         
12035         if(store){
12036             this.refresh();
12037         }
12038     },
12039     /**
12040      * onbeforeLoad - masks the loading area.
12041      *
12042      */
12043     onBeforeLoad : function(store,opts)
12044     {
12045          Roo.log('onBeforeLoad');   
12046         if (!opts.add) {
12047             this.el.update("");
12048         }
12049         this.el.mask(this.mask ? this.mask : "Loading" ); 
12050     },
12051     onLoad : function ()
12052     {
12053         this.el.unmask();
12054     },
12055     
12056
12057     /**
12058      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12059      * @param {HTMLElement} node
12060      * @return {HTMLElement} The template node
12061      */
12062     findItemFromChild : function(node){
12063         var el = this.dataName  ?
12064             this.el.child('.roo-tpl-' + this.dataName,true) :
12065             this.el.dom; 
12066         
12067         if(!node || node.parentNode == el){
12068                     return node;
12069             }
12070             var p = node.parentNode;
12071             while(p && p != el){
12072             if(p.parentNode == el){
12073                 return p;
12074             }
12075             p = p.parentNode;
12076         }
12077             return null;
12078     },
12079
12080     /** @ignore */
12081     onClick : function(e){
12082         var item = this.findItemFromChild(e.getTarget());
12083         if(item){
12084             var index = this.indexOf(item);
12085             if(this.onItemClick(item, index, e) !== false){
12086                 this.fireEvent("click", this, index, item, e);
12087             }
12088         }else{
12089             this.clearSelections();
12090         }
12091     },
12092
12093     /** @ignore */
12094     onContextMenu : function(e){
12095         var item = this.findItemFromChild(e.getTarget());
12096         if(item){
12097             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12098         }
12099     },
12100
12101     /** @ignore */
12102     onDblClick : function(e){
12103         var item = this.findItemFromChild(e.getTarget());
12104         if(item){
12105             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12106         }
12107     },
12108
12109     onItemClick : function(item, index, e)
12110     {
12111         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12112             return false;
12113         }
12114         if (this.toggleSelect) {
12115             var m = this.isSelected(item) ? 'unselect' : 'select';
12116             Roo.log(m);
12117             var _t = this;
12118             _t[m](item, true, false);
12119             return true;
12120         }
12121         if(this.multiSelect || this.singleSelect){
12122             if(this.multiSelect && e.shiftKey && this.lastSelection){
12123                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12124             }else{
12125                 this.select(item, this.multiSelect && e.ctrlKey);
12126                 this.lastSelection = item;
12127             }
12128             
12129             if(!this.tickable){
12130                 e.preventDefault();
12131             }
12132             
12133         }
12134         return true;
12135     },
12136
12137     /**
12138      * Get the number of selected nodes.
12139      * @return {Number}
12140      */
12141     getSelectionCount : function(){
12142         return this.selections.length;
12143     },
12144
12145     /**
12146      * Get the currently selected nodes.
12147      * @return {Array} An array of HTMLElements
12148      */
12149     getSelectedNodes : function(){
12150         return this.selections;
12151     },
12152
12153     /**
12154      * Get the indexes of the selected nodes.
12155      * @return {Array}
12156      */
12157     getSelectedIndexes : function(){
12158         var indexes = [], s = this.selections;
12159         for(var i = 0, len = s.length; i < len; i++){
12160             indexes.push(s[i].nodeIndex);
12161         }
12162         return indexes;
12163     },
12164
12165     /**
12166      * Clear all selections
12167      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12168      */
12169     clearSelections : function(suppressEvent){
12170         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12171             this.cmp.elements = this.selections;
12172             this.cmp.removeClass(this.selectedClass);
12173             this.selections = [];
12174             if(!suppressEvent){
12175                 this.fireEvent("selectionchange", this, this.selections);
12176             }
12177         }
12178     },
12179
12180     /**
12181      * Returns true if the passed node is selected
12182      * @param {HTMLElement/Number} node The node or node index
12183      * @return {Boolean}
12184      */
12185     isSelected : function(node){
12186         var s = this.selections;
12187         if(s.length < 1){
12188             return false;
12189         }
12190         node = this.getNode(node);
12191         return s.indexOf(node) !== -1;
12192     },
12193
12194     /**
12195      * Selects nodes.
12196      * @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
12197      * @param {Boolean} keepExisting (optional) true to keep existing selections
12198      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12199      */
12200     select : function(nodeInfo, keepExisting, suppressEvent){
12201         if(nodeInfo instanceof Array){
12202             if(!keepExisting){
12203                 this.clearSelections(true);
12204             }
12205             for(var i = 0, len = nodeInfo.length; i < len; i++){
12206                 this.select(nodeInfo[i], true, true);
12207             }
12208             return;
12209         } 
12210         var node = this.getNode(nodeInfo);
12211         if(!node || this.isSelected(node)){
12212             return; // already selected.
12213         }
12214         if(!keepExisting){
12215             this.clearSelections(true);
12216         }
12217         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12218             Roo.fly(node).addClass(this.selectedClass);
12219             this.selections.push(node);
12220             if(!suppressEvent){
12221                 this.fireEvent("selectionchange", this, this.selections);
12222             }
12223         }
12224         
12225         
12226     },
12227       /**
12228      * Unselects nodes.
12229      * @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
12230      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12231      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12232      */
12233     unselect : function(nodeInfo, keepExisting, suppressEvent)
12234     {
12235         if(nodeInfo instanceof Array){
12236             Roo.each(this.selections, function(s) {
12237                 this.unselect(s, nodeInfo);
12238             }, this);
12239             return;
12240         }
12241         var node = this.getNode(nodeInfo);
12242         if(!node || !this.isSelected(node)){
12243             Roo.log("not selected");
12244             return; // not selected.
12245         }
12246         // fireevent???
12247         var ns = [];
12248         Roo.each(this.selections, function(s) {
12249             if (s == node ) {
12250                 Roo.fly(node).removeClass(this.selectedClass);
12251
12252                 return;
12253             }
12254             ns.push(s);
12255         },this);
12256         
12257         this.selections= ns;
12258         this.fireEvent("selectionchange", this, this.selections);
12259     },
12260
12261     /**
12262      * Gets a template node.
12263      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12264      * @return {HTMLElement} The node or null if it wasn't found
12265      */
12266     getNode : function(nodeInfo){
12267         if(typeof nodeInfo == "string"){
12268             return document.getElementById(nodeInfo);
12269         }else if(typeof nodeInfo == "number"){
12270             return this.nodes[nodeInfo];
12271         }
12272         return nodeInfo;
12273     },
12274
12275     /**
12276      * Gets a range template nodes.
12277      * @param {Number} startIndex
12278      * @param {Number} endIndex
12279      * @return {Array} An array of nodes
12280      */
12281     getNodes : function(start, end){
12282         var ns = this.nodes;
12283         start = start || 0;
12284         end = typeof end == "undefined" ? ns.length - 1 : end;
12285         var nodes = [];
12286         if(start <= end){
12287             for(var i = start; i <= end; i++){
12288                 nodes.push(ns[i]);
12289             }
12290         } else{
12291             for(var i = start; i >= end; i--){
12292                 nodes.push(ns[i]);
12293             }
12294         }
12295         return nodes;
12296     },
12297
12298     /**
12299      * Finds the index of the passed node
12300      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12301      * @return {Number} The index of the node or -1
12302      */
12303     indexOf : function(node){
12304         node = this.getNode(node);
12305         if(typeof node.nodeIndex == "number"){
12306             return node.nodeIndex;
12307         }
12308         var ns = this.nodes;
12309         for(var i = 0, len = ns.length; i < len; i++){
12310             if(ns[i] == node){
12311                 return i;
12312             }
12313         }
12314         return -1;
12315     }
12316 });
12317 /*
12318  * - LGPL
12319  *
12320  * based on jquery fullcalendar
12321  * 
12322  */
12323
12324 Roo.bootstrap = Roo.bootstrap || {};
12325 /**
12326  * @class Roo.bootstrap.Calendar
12327  * @extends Roo.bootstrap.Component
12328  * Bootstrap Calendar class
12329  * @cfg {Boolean} loadMask (true|false) default false
12330  * @cfg {Object} header generate the user specific header of the calendar, default false
12331
12332  * @constructor
12333  * Create a new Container
12334  * @param {Object} config The config object
12335  */
12336
12337
12338
12339 Roo.bootstrap.Calendar = function(config){
12340     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12341      this.addEvents({
12342         /**
12343              * @event select
12344              * Fires when a date is selected
12345              * @param {DatePicker} this
12346              * @param {Date} date The selected date
12347              */
12348         'select': true,
12349         /**
12350              * @event monthchange
12351              * Fires when the displayed month changes 
12352              * @param {DatePicker} this
12353              * @param {Date} date The selected month
12354              */
12355         'monthchange': true,
12356         /**
12357              * @event evententer
12358              * Fires when mouse over an event
12359              * @param {Calendar} this
12360              * @param {event} Event
12361              */
12362         'evententer': true,
12363         /**
12364              * @event eventleave
12365              * Fires when the mouse leaves an
12366              * @param {Calendar} this
12367              * @param {event}
12368              */
12369         'eventleave': true,
12370         /**
12371              * @event eventclick
12372              * Fires when the mouse click an
12373              * @param {Calendar} this
12374              * @param {event}
12375              */
12376         'eventclick': true
12377         
12378     });
12379
12380 };
12381
12382 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12383     
12384      /**
12385      * @cfg {Number} startDay
12386      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12387      */
12388     startDay : 0,
12389     
12390     loadMask : false,
12391     
12392     header : false,
12393       
12394     getAutoCreate : function(){
12395         
12396         
12397         var fc_button = function(name, corner, style, content ) {
12398             return Roo.apply({},{
12399                 tag : 'span',
12400                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12401                          (corner.length ?
12402                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12403                             ''
12404                         ),
12405                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12406                 unselectable: 'on'
12407             });
12408         };
12409         
12410         var header = {};
12411         
12412         if(!this.header){
12413             header = {
12414                 tag : 'table',
12415                 cls : 'fc-header',
12416                 style : 'width:100%',
12417                 cn : [
12418                     {
12419                         tag: 'tr',
12420                         cn : [
12421                             {
12422                                 tag : 'td',
12423                                 cls : 'fc-header-left',
12424                                 cn : [
12425                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12426                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12427                                     { tag: 'span', cls: 'fc-header-space' },
12428                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12429
12430
12431                                 ]
12432                             },
12433
12434                             {
12435                                 tag : 'td',
12436                                 cls : 'fc-header-center',
12437                                 cn : [
12438                                     {
12439                                         tag: 'span',
12440                                         cls: 'fc-header-title',
12441                                         cn : {
12442                                             tag: 'H2',
12443                                             html : 'month / year'
12444                                         }
12445                                     }
12446
12447                                 ]
12448                             },
12449                             {
12450                                 tag : 'td',
12451                                 cls : 'fc-header-right',
12452                                 cn : [
12453                               /*      fc_button('month', 'left', '', 'month' ),
12454                                     fc_button('week', '', '', 'week' ),
12455                                     fc_button('day', 'right', '', 'day' )
12456                                 */    
12457
12458                                 ]
12459                             }
12460
12461                         ]
12462                     }
12463                 ]
12464             };
12465         }
12466         
12467         header = this.header;
12468         
12469        
12470         var cal_heads = function() {
12471             var ret = [];
12472             // fixme - handle this.
12473             
12474             for (var i =0; i < Date.dayNames.length; i++) {
12475                 var d = Date.dayNames[i];
12476                 ret.push({
12477                     tag: 'th',
12478                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12479                     html : d.substring(0,3)
12480                 });
12481                 
12482             }
12483             ret[0].cls += ' fc-first';
12484             ret[6].cls += ' fc-last';
12485             return ret;
12486         };
12487         var cal_cell = function(n) {
12488             return  {
12489                 tag: 'td',
12490                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12491                 cn : [
12492                     {
12493                         cn : [
12494                             {
12495                                 cls: 'fc-day-number',
12496                                 html: 'D'
12497                             },
12498                             {
12499                                 cls: 'fc-day-content',
12500                              
12501                                 cn : [
12502                                      {
12503                                         style: 'position: relative;' // height: 17px;
12504                                     }
12505                                 ]
12506                             }
12507                             
12508                             
12509                         ]
12510                     }
12511                 ]
12512                 
12513             }
12514         };
12515         var cal_rows = function() {
12516             
12517             var ret = []
12518             for (var r = 0; r < 6; r++) {
12519                 var row= {
12520                     tag : 'tr',
12521                     cls : 'fc-week',
12522                     cn : []
12523                 };
12524                 
12525                 for (var i =0; i < Date.dayNames.length; i++) {
12526                     var d = Date.dayNames[i];
12527                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12528
12529                 }
12530                 row.cn[0].cls+=' fc-first';
12531                 row.cn[0].cn[0].style = 'min-height:90px';
12532                 row.cn[6].cls+=' fc-last';
12533                 ret.push(row);
12534                 
12535             }
12536             ret[0].cls += ' fc-first';
12537             ret[4].cls += ' fc-prev-last';
12538             ret[5].cls += ' fc-last';
12539             return ret;
12540             
12541         };
12542         
12543         var cal_table = {
12544             tag: 'table',
12545             cls: 'fc-border-separate',
12546             style : 'width:100%',
12547             cellspacing  : 0,
12548             cn : [
12549                 { 
12550                     tag: 'thead',
12551                     cn : [
12552                         { 
12553                             tag: 'tr',
12554                             cls : 'fc-first fc-last',
12555                             cn : cal_heads()
12556                         }
12557                     ]
12558                 },
12559                 { 
12560                     tag: 'tbody',
12561                     cn : cal_rows()
12562                 }
12563                   
12564             ]
12565         };
12566          
12567          var cfg = {
12568             cls : 'fc fc-ltr',
12569             cn : [
12570                 header,
12571                 {
12572                     cls : 'fc-content',
12573                     style : "position: relative;",
12574                     cn : [
12575                         {
12576                             cls : 'fc-view fc-view-month fc-grid',
12577                             style : 'position: relative',
12578                             unselectable : 'on',
12579                             cn : [
12580                                 {
12581                                     cls : 'fc-event-container',
12582                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12583                                 },
12584                                 cal_table
12585                             ]
12586                         }
12587                     ]
12588     
12589                 }
12590            ] 
12591             
12592         };
12593         
12594          
12595         
12596         return cfg;
12597     },
12598     
12599     
12600     initEvents : function()
12601     {
12602         if(!this.store){
12603             throw "can not find store for calendar";
12604         }
12605         
12606         var mark = {
12607             tag: "div",
12608             cls:"x-dlg-mask",
12609             style: "text-align:center",
12610             cn: [
12611                 {
12612                     tag: "div",
12613                     style: "background-color:white;width:50%;margin:250 auto",
12614                     cn: [
12615                         {
12616                             tag: "img",
12617                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12618                         },
12619                         {
12620                             tag: "span",
12621                             html: "Loading"
12622                         }
12623                         
12624                     ]
12625                 }
12626             ]
12627         }
12628         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12629         
12630         var size = this.el.select('.fc-content', true).first().getSize();
12631         this.maskEl.setSize(size.width, size.height);
12632         this.maskEl.enableDisplayMode("block");
12633         if(!this.loadMask){
12634             this.maskEl.hide();
12635         }
12636         
12637         this.store = Roo.factory(this.store, Roo.data);
12638         this.store.on('load', this.onLoad, this);
12639         this.store.on('beforeload', this.onBeforeLoad, this);
12640         
12641         this.resize();
12642         
12643         this.cells = this.el.select('.fc-day',true);
12644         //Roo.log(this.cells);
12645         this.textNodes = this.el.query('.fc-day-number');
12646         this.cells.addClassOnOver('fc-state-hover');
12647         
12648         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12649         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12650         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12651         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12652         
12653         this.on('monthchange', this.onMonthChange, this);
12654         
12655         this.update(new Date().clearTime());
12656     },
12657     
12658     resize : function() {
12659         var sz  = this.el.getSize();
12660         
12661         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12662         this.el.select('.fc-day-content div',true).setHeight(34);
12663     },
12664     
12665     
12666     // private
12667     showPrevMonth : function(e){
12668         this.update(this.activeDate.add("mo", -1));
12669     },
12670     showToday : function(e){
12671         this.update(new Date().clearTime());
12672     },
12673     // private
12674     showNextMonth : function(e){
12675         this.update(this.activeDate.add("mo", 1));
12676     },
12677
12678     // private
12679     showPrevYear : function(){
12680         this.update(this.activeDate.add("y", -1));
12681     },
12682
12683     // private
12684     showNextYear : function(){
12685         this.update(this.activeDate.add("y", 1));
12686     },
12687
12688     
12689    // private
12690     update : function(date)
12691     {
12692         var vd = this.activeDate;
12693         this.activeDate = date;
12694 //        if(vd && this.el){
12695 //            var t = date.getTime();
12696 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12697 //                Roo.log('using add remove');
12698 //                
12699 //                this.fireEvent('monthchange', this, date);
12700 //                
12701 //                this.cells.removeClass("fc-state-highlight");
12702 //                this.cells.each(function(c){
12703 //                   if(c.dateValue == t){
12704 //                       c.addClass("fc-state-highlight");
12705 //                       setTimeout(function(){
12706 //                            try{c.dom.firstChild.focus();}catch(e){}
12707 //                       }, 50);
12708 //                       return false;
12709 //                   }
12710 //                   return true;
12711 //                });
12712 //                return;
12713 //            }
12714 //        }
12715         
12716         var days = date.getDaysInMonth();
12717         
12718         var firstOfMonth = date.getFirstDateOfMonth();
12719         var startingPos = firstOfMonth.getDay()-this.startDay;
12720         
12721         if(startingPos < this.startDay){
12722             startingPos += 7;
12723         }
12724         
12725         var pm = date.add(Date.MONTH, -1);
12726         var prevStart = pm.getDaysInMonth()-startingPos;
12727 //        
12728         this.cells = this.el.select('.fc-day',true);
12729         this.textNodes = this.el.query('.fc-day-number');
12730         this.cells.addClassOnOver('fc-state-hover');
12731         
12732         var cells = this.cells.elements;
12733         var textEls = this.textNodes;
12734         
12735         Roo.each(cells, function(cell){
12736             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
12737         });
12738         
12739         days += startingPos;
12740
12741         // convert everything to numbers so it's fast
12742         var day = 86400000;
12743         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
12744         //Roo.log(d);
12745         //Roo.log(pm);
12746         //Roo.log(prevStart);
12747         
12748         var today = new Date().clearTime().getTime();
12749         var sel = date.clearTime().getTime();
12750         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
12751         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
12752         var ddMatch = this.disabledDatesRE;
12753         var ddText = this.disabledDatesText;
12754         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
12755         var ddaysText = this.disabledDaysText;
12756         var format = this.format;
12757         
12758         var setCellClass = function(cal, cell){
12759             cell.row = 0;
12760             cell.events = [];
12761             cell.more = [];
12762             //Roo.log('set Cell Class');
12763             cell.title = "";
12764             var t = d.getTime();
12765             
12766             //Roo.log(d);
12767             
12768             cell.dateValue = t;
12769             if(t == today){
12770                 cell.className += " fc-today";
12771                 cell.className += " fc-state-highlight";
12772                 cell.title = cal.todayText;
12773             }
12774             if(t == sel){
12775                 // disable highlight in other month..
12776                 //cell.className += " fc-state-highlight";
12777                 
12778             }
12779             // disabling
12780             if(t < min) {
12781                 cell.className = " fc-state-disabled";
12782                 cell.title = cal.minText;
12783                 return;
12784             }
12785             if(t > max) {
12786                 cell.className = " fc-state-disabled";
12787                 cell.title = cal.maxText;
12788                 return;
12789             }
12790             if(ddays){
12791                 if(ddays.indexOf(d.getDay()) != -1){
12792                     cell.title = ddaysText;
12793                     cell.className = " fc-state-disabled";
12794                 }
12795             }
12796             if(ddMatch && format){
12797                 var fvalue = d.dateFormat(format);
12798                 if(ddMatch.test(fvalue)){
12799                     cell.title = ddText.replace("%0", fvalue);
12800                     cell.className = " fc-state-disabled";
12801                 }
12802             }
12803             
12804             if (!cell.initialClassName) {
12805                 cell.initialClassName = cell.dom.className;
12806             }
12807             
12808             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
12809         };
12810
12811         var i = 0;
12812         
12813         for(; i < startingPos; i++) {
12814             textEls[i].innerHTML = (++prevStart);
12815             d.setDate(d.getDate()+1);
12816             
12817             cells[i].className = "fc-past fc-other-month";
12818             setCellClass(this, cells[i]);
12819         }
12820         
12821         var intDay = 0;
12822         
12823         for(; i < days; i++){
12824             intDay = i - startingPos + 1;
12825             textEls[i].innerHTML = (intDay);
12826             d.setDate(d.getDate()+1);
12827             
12828             cells[i].className = ''; // "x-date-active";
12829             setCellClass(this, cells[i]);
12830         }
12831         var extraDays = 0;
12832         
12833         for(; i < 42; i++) {
12834             textEls[i].innerHTML = (++extraDays);
12835             d.setDate(d.getDate()+1);
12836             
12837             cells[i].className = "fc-future fc-other-month";
12838             setCellClass(this, cells[i]);
12839         }
12840         
12841         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
12842         
12843         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
12844         
12845         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
12846         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
12847         
12848         if(totalRows != 6){
12849             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
12850             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
12851         }
12852         
12853         this.fireEvent('monthchange', this, date);
12854         
12855         
12856         /*
12857         if(!this.internalRender){
12858             var main = this.el.dom.firstChild;
12859             var w = main.offsetWidth;
12860             this.el.setWidth(w + this.el.getBorderWidth("lr"));
12861             Roo.fly(main).setWidth(w);
12862             this.internalRender = true;
12863             // opera does not respect the auto grow header center column
12864             // then, after it gets a width opera refuses to recalculate
12865             // without a second pass
12866             if(Roo.isOpera && !this.secondPass){
12867                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
12868                 this.secondPass = true;
12869                 this.update.defer(10, this, [date]);
12870             }
12871         }
12872         */
12873         
12874     },
12875     
12876     findCell : function(dt) {
12877         dt = dt.clearTime().getTime();
12878         var ret = false;
12879         this.cells.each(function(c){
12880             //Roo.log("check " +c.dateValue + '?=' + dt);
12881             if(c.dateValue == dt){
12882                 ret = c;
12883                 return false;
12884             }
12885             return true;
12886         });
12887         
12888         return ret;
12889     },
12890     
12891     findCells : function(ev) {
12892         var s = ev.start.clone().clearTime().getTime();
12893        // Roo.log(s);
12894         var e= ev.end.clone().clearTime().getTime();
12895        // Roo.log(e);
12896         var ret = [];
12897         this.cells.each(function(c){
12898              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
12899             
12900             if(c.dateValue > e){
12901                 return ;
12902             }
12903             if(c.dateValue < s){
12904                 return ;
12905             }
12906             ret.push(c);
12907         });
12908         
12909         return ret;    
12910     },
12911     
12912 //    findBestRow: function(cells)
12913 //    {
12914 //        var ret = 0;
12915 //        
12916 //        for (var i =0 ; i < cells.length;i++) {
12917 //            ret  = Math.max(cells[i].rows || 0,ret);
12918 //        }
12919 //        return ret;
12920 //        
12921 //    },
12922     
12923     
12924     addItem : function(ev)
12925     {
12926         // look for vertical location slot in
12927         var cells = this.findCells(ev);
12928         
12929 //        ev.row = this.findBestRow(cells);
12930         
12931         // work out the location.
12932         
12933         var crow = false;
12934         var rows = [];
12935         for(var i =0; i < cells.length; i++) {
12936             
12937             cells[i].row = cells[0].row;
12938             
12939             if(i == 0){
12940                 cells[i].row = cells[i].row + 1;
12941             }
12942             
12943             if (!crow) {
12944                 crow = {
12945                     start : cells[i],
12946                     end :  cells[i]
12947                 };
12948                 continue;
12949             }
12950             if (crow.start.getY() == cells[i].getY()) {
12951                 // on same row.
12952                 crow.end = cells[i];
12953                 continue;
12954             }
12955             // different row.
12956             rows.push(crow);
12957             crow = {
12958                 start: cells[i],
12959                 end : cells[i]
12960             };
12961             
12962         }
12963         
12964         rows.push(crow);
12965         ev.els = [];
12966         ev.rows = rows;
12967         ev.cells = cells;
12968         
12969         cells[0].events.push(ev);
12970         
12971         this.calevents.push(ev);
12972     },
12973     
12974     clearEvents: function() {
12975         
12976         if(!this.calevents){
12977             return;
12978         }
12979         
12980         Roo.each(this.cells.elements, function(c){
12981             c.row = 0;
12982             c.events = [];
12983             c.more = [];
12984         });
12985         
12986         Roo.each(this.calevents, function(e) {
12987             Roo.each(e.els, function(el) {
12988                 el.un('mouseenter' ,this.onEventEnter, this);
12989                 el.un('mouseleave' ,this.onEventLeave, this);
12990                 el.remove();
12991             },this);
12992         },this);
12993         
12994         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
12995             e.remove();
12996         });
12997         
12998     },
12999     
13000     renderEvents: function()
13001     {   
13002         var _this = this;
13003         
13004         this.cells.each(function(c) {
13005             
13006             if(c.row < 5){
13007                 return;
13008             }
13009             
13010             var ev = c.events;
13011             
13012             var r = 4;
13013             if(c.row != c.events.length){
13014                 r = 4 - (4 - (c.row - c.events.length));
13015             }
13016             
13017             c.events = ev.slice(0, r);
13018             c.more = ev.slice(r);
13019             
13020             if(c.more.length && c.more.length == 1){
13021                 c.events.push(c.more.pop());
13022             }
13023             
13024             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13025             
13026         });
13027             
13028         this.cells.each(function(c) {
13029             
13030             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13031             
13032             
13033             for (var e = 0; e < c.events.length; e++){
13034                 var ev = c.events[e];
13035                 var rows = ev.rows;
13036                 
13037                 for(var i = 0; i < rows.length; i++) {
13038                 
13039                     // how many rows should it span..
13040
13041                     var  cfg = {
13042                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13043                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13044
13045                         unselectable : "on",
13046                         cn : [
13047                             {
13048                                 cls: 'fc-event-inner',
13049                                 cn : [
13050     //                                {
13051     //                                  tag:'span',
13052     //                                  cls: 'fc-event-time',
13053     //                                  html : cells.length > 1 ? '' : ev.time
13054     //                                },
13055                                     {
13056                                       tag:'span',
13057                                       cls: 'fc-event-title',
13058                                       html : String.format('{0}', ev.title)
13059                                     }
13060
13061
13062                                 ]
13063                             },
13064                             {
13065                                 cls: 'ui-resizable-handle ui-resizable-e',
13066                                 html : '&nbsp;&nbsp;&nbsp'
13067                             }
13068
13069                         ]
13070                     };
13071
13072                     if (i == 0) {
13073                         cfg.cls += ' fc-event-start';
13074                     }
13075                     if ((i+1) == rows.length) {
13076                         cfg.cls += ' fc-event-end';
13077                     }
13078
13079                     var ctr = _this.el.select('.fc-event-container',true).first();
13080                     var cg = ctr.createChild(cfg);
13081
13082                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13083                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13084
13085                     var r = (c.more.length) ? 1 : 0;
13086                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13087                     cg.setWidth(ebox.right - sbox.x -2);
13088
13089                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13090                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13091                     cg.on('click', _this.onEventClick, _this, ev);
13092
13093                     ev.els.push(cg);
13094                     
13095                 }
13096                 
13097             }
13098             
13099             
13100             if(c.more.length){
13101                 var  cfg = {
13102                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13103                     style : 'position: absolute',
13104                     unselectable : "on",
13105                     cn : [
13106                         {
13107                             cls: 'fc-event-inner',
13108                             cn : [
13109                                 {
13110                                   tag:'span',
13111                                   cls: 'fc-event-title',
13112                                   html : 'More'
13113                                 }
13114
13115
13116                             ]
13117                         },
13118                         {
13119                             cls: 'ui-resizable-handle ui-resizable-e',
13120                             html : '&nbsp;&nbsp;&nbsp'
13121                         }
13122
13123                     ]
13124                 };
13125
13126                 var ctr = _this.el.select('.fc-event-container',true).first();
13127                 var cg = ctr.createChild(cfg);
13128
13129                 var sbox = c.select('.fc-day-content',true).first().getBox();
13130                 var ebox = c.select('.fc-day-content',true).first().getBox();
13131                 //Roo.log(cg);
13132                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13133                 cg.setWidth(ebox.right - sbox.x -2);
13134
13135                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13136                 
13137             }
13138             
13139         });
13140         
13141         
13142         
13143     },
13144     
13145     onEventEnter: function (e, el,event,d) {
13146         this.fireEvent('evententer', this, el, event);
13147     },
13148     
13149     onEventLeave: function (e, el,event,d) {
13150         this.fireEvent('eventleave', this, el, event);
13151     },
13152     
13153     onEventClick: function (e, el,event,d) {
13154         this.fireEvent('eventclick', this, el, event);
13155     },
13156     
13157     onMonthChange: function () {
13158         this.store.load();
13159     },
13160     
13161     onMoreEventClick: function(e, el, more)
13162     {
13163         var _this = this;
13164         
13165         this.calpopover.placement = 'right';
13166         this.calpopover.setTitle('More');
13167         
13168         this.calpopover.setContent('');
13169         
13170         var ctr = this.calpopover.el.select('.popover-content', true).first();
13171         
13172         Roo.each(more, function(m){
13173             var cfg = {
13174                 cls : 'fc-event-hori fc-event-draggable',
13175                 html : m.title
13176             }
13177             var cg = ctr.createChild(cfg);
13178             
13179             cg.on('click', _this.onEventClick, _this, m);
13180         });
13181         
13182         this.calpopover.show(el);
13183         
13184         
13185     },
13186     
13187     onLoad: function () 
13188     {   
13189         this.calevents = [];
13190         var cal = this;
13191         
13192         if(this.store.getCount() > 0){
13193             this.store.data.each(function(d){
13194                cal.addItem({
13195                     id : d.data.id,
13196                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13197                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13198                     time : d.data.start_time,
13199                     title : d.data.title,
13200                     description : d.data.description,
13201                     venue : d.data.venue
13202                 });
13203             });
13204         }
13205         
13206         this.renderEvents();
13207         
13208         if(this.calevents.length && this.loadMask){
13209             this.maskEl.hide();
13210         }
13211     },
13212     
13213     onBeforeLoad: function()
13214     {
13215         this.clearEvents();
13216         if(this.loadMask){
13217             this.maskEl.show();
13218         }
13219     }
13220 });
13221
13222  
13223  /*
13224  * - LGPL
13225  *
13226  * element
13227  * 
13228  */
13229
13230 /**
13231  * @class Roo.bootstrap.Popover
13232  * @extends Roo.bootstrap.Component
13233  * Bootstrap Popover class
13234  * @cfg {String} html contents of the popover   (or false to use children..)
13235  * @cfg {String} title of popover (or false to hide)
13236  * @cfg {String} placement how it is placed
13237  * @cfg {String} trigger click || hover (or false to trigger manually)
13238  * @cfg {String} over what (parent or false to trigger manually.)
13239  * 
13240  * @constructor
13241  * Create a new Popover
13242  * @param {Object} config The config object
13243  */
13244
13245 Roo.bootstrap.Popover = function(config){
13246     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13247 };
13248
13249 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13250     
13251     title: 'Fill in a title',
13252     html: false,
13253     
13254     placement : 'right',
13255     trigger : 'hover', // hover
13256     
13257     over: 'parent',
13258     
13259     can_build_overlaid : false,
13260     
13261     getChildContainer : function()
13262     {
13263         return this.el.select('.popover-content',true).first();
13264     },
13265     
13266     getAutoCreate : function(){
13267          Roo.log('make popover?');
13268         var cfg = {
13269            cls : 'popover roo-dynamic',
13270            style: 'display:block',
13271            cn : [
13272                 {
13273                     cls : 'arrow'
13274                 },
13275                 {
13276                     cls : 'popover-inner',
13277                     cn : [
13278                         {
13279                             tag: 'h3',
13280                             cls: 'popover-title',
13281                             html : this.title
13282                         },
13283                         {
13284                             cls : 'popover-content',
13285                             html : this.html
13286                         }
13287                     ]
13288                     
13289                 }
13290            ]
13291         };
13292         
13293         return cfg;
13294     },
13295     setTitle: function(str)
13296     {
13297         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13298     },
13299     setContent: function(str)
13300     {
13301         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13302     },
13303     // as it get's added to the bottom of the page.
13304     onRender : function(ct, position)
13305     {
13306         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13307         if(!this.el){
13308             var cfg = Roo.apply({},  this.getAutoCreate());
13309             cfg.id = Roo.id();
13310             
13311             if (this.cls) {
13312                 cfg.cls += ' ' + this.cls;
13313             }
13314             if (this.style) {
13315                 cfg.style = this.style;
13316             }
13317             Roo.log("adding to ")
13318             this.el = Roo.get(document.body).createChild(cfg, position);
13319             Roo.log(this.el);
13320         }
13321         this.initEvents();
13322     },
13323     
13324     initEvents : function()
13325     {
13326         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13327         this.el.enableDisplayMode('block');
13328         this.el.hide();
13329         if (this.over === false) {
13330             return; 
13331         }
13332         if (this.triggers === false) {
13333             return;
13334         }
13335         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13336         var triggers = this.trigger ? this.trigger.split(' ') : [];
13337         Roo.each(triggers, function(trigger) {
13338         
13339             if (trigger == 'click') {
13340                 on_el.on('click', this.toggle, this);
13341             } else if (trigger != 'manual') {
13342                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13343                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13344       
13345                 on_el.on(eventIn  ,this.enter, this);
13346                 on_el.on(eventOut, this.leave, this);
13347             }
13348         }, this);
13349         
13350     },
13351     
13352     
13353     // private
13354     timeout : null,
13355     hoverState : null,
13356     
13357     toggle : function () {
13358         this.hoverState == 'in' ? this.leave() : this.enter();
13359     },
13360     
13361     enter : function () {
13362        
13363     
13364         clearTimeout(this.timeout);
13365     
13366         this.hoverState = 'in'
13367     
13368         if (!this.delay || !this.delay.show) {
13369             this.show();
13370             return 
13371         }
13372         var _t = this;
13373         this.timeout = setTimeout(function () {
13374             if (_t.hoverState == 'in') {
13375                 _t.show();
13376             }
13377         }, this.delay.show)
13378     },
13379     leave : function() {
13380         clearTimeout(this.timeout);
13381     
13382         this.hoverState = 'out'
13383     
13384         if (!this.delay || !this.delay.hide) {
13385             this.hide();
13386             return 
13387         }
13388         var _t = this;
13389         this.timeout = setTimeout(function () {
13390             if (_t.hoverState == 'out') {
13391                 _t.hide();
13392             }
13393         }, this.delay.hide)
13394     },
13395     
13396     show : function (on_el)
13397     {
13398         if (!on_el) {
13399             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13400         }
13401         // set content.
13402         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13403         if (this.html !== false) {
13404             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13405         }
13406         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13407         if (!this.title.length) {
13408             this.el.select('.popover-title',true).hide();
13409         }
13410         
13411         var placement = typeof this.placement == 'function' ?
13412             this.placement.call(this, this.el, on_el) :
13413             this.placement;
13414             
13415         var autoToken = /\s?auto?\s?/i;
13416         var autoPlace = autoToken.test(placement);
13417         if (autoPlace) {
13418             placement = placement.replace(autoToken, '') || 'top';
13419         }
13420         
13421         //this.el.detach()
13422         //this.el.setXY([0,0]);
13423         this.el.show();
13424         this.el.dom.style.display='block';
13425         this.el.addClass(placement);
13426         
13427         //this.el.appendTo(on_el);
13428         
13429         var p = this.getPosition();
13430         var box = this.el.getBox();
13431         
13432         if (autoPlace) {
13433             // fixme..
13434         }
13435         var align = Roo.bootstrap.Popover.alignment[placement]
13436         this.el.alignTo(on_el, align[0],align[1]);
13437         //var arrow = this.el.select('.arrow',true).first();
13438         //arrow.set(align[2], 
13439         
13440         this.el.addClass('in');
13441         this.hoverState = null;
13442         
13443         if (this.el.hasClass('fade')) {
13444             // fade it?
13445         }
13446         
13447     },
13448     hide : function()
13449     {
13450         this.el.setXY([0,0]);
13451         this.el.removeClass('in');
13452         this.el.hide();
13453         
13454     }
13455     
13456 });
13457
13458 Roo.bootstrap.Popover.alignment = {
13459     'left' : ['r-l', [-10,0], 'right'],
13460     'right' : ['l-r', [10,0], 'left'],
13461     'bottom' : ['t-b', [0,10], 'top'],
13462     'top' : [ 'b-t', [0,-10], 'bottom']
13463 };
13464
13465  /*
13466  * - LGPL
13467  *
13468  * Progress
13469  * 
13470  */
13471
13472 /**
13473  * @class Roo.bootstrap.Progress
13474  * @extends Roo.bootstrap.Component
13475  * Bootstrap Progress class
13476  * @cfg {Boolean} striped striped of the progress bar
13477  * @cfg {Boolean} active animated of the progress bar
13478  * 
13479  * 
13480  * @constructor
13481  * Create a new Progress
13482  * @param {Object} config The config object
13483  */
13484
13485 Roo.bootstrap.Progress = function(config){
13486     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13487 };
13488
13489 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13490     
13491     striped : false,
13492     active: false,
13493     
13494     getAutoCreate : function(){
13495         var cfg = {
13496             tag: 'div',
13497             cls: 'progress'
13498         };
13499         
13500         
13501         if(this.striped){
13502             cfg.cls += ' progress-striped';
13503         }
13504       
13505         if(this.active){
13506             cfg.cls += ' active';
13507         }
13508         
13509         
13510         return cfg;
13511     }
13512    
13513 });
13514
13515  
13516
13517  /*
13518  * - LGPL
13519  *
13520  * ProgressBar
13521  * 
13522  */
13523
13524 /**
13525  * @class Roo.bootstrap.ProgressBar
13526  * @extends Roo.bootstrap.Component
13527  * Bootstrap ProgressBar class
13528  * @cfg {Number} aria_valuenow aria-value now
13529  * @cfg {Number} aria_valuemin aria-value min
13530  * @cfg {Number} aria_valuemax aria-value max
13531  * @cfg {String} label label for the progress bar
13532  * @cfg {String} panel (success | info | warning | danger )
13533  * @cfg {String} role role of the progress bar
13534  * @cfg {String} sr_only text
13535  * 
13536  * 
13537  * @constructor
13538  * Create a new ProgressBar
13539  * @param {Object} config The config object
13540  */
13541
13542 Roo.bootstrap.ProgressBar = function(config){
13543     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13544 };
13545
13546 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13547     
13548     aria_valuenow : 0,
13549     aria_valuemin : 0,
13550     aria_valuemax : 100,
13551     label : false,
13552     panel : false,
13553     role : false,
13554     sr_only: false,
13555     
13556     getAutoCreate : function()
13557     {
13558         
13559         var cfg = {
13560             tag: 'div',
13561             cls: 'progress-bar',
13562             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13563         };
13564         
13565         if(this.sr_only){
13566             cfg.cn = {
13567                 tag: 'span',
13568                 cls: 'sr-only',
13569                 html: this.sr_only
13570             }
13571         }
13572         
13573         if(this.role){
13574             cfg.role = this.role;
13575         }
13576         
13577         if(this.aria_valuenow){
13578             cfg['aria-valuenow'] = this.aria_valuenow;
13579         }
13580         
13581         if(this.aria_valuemin){
13582             cfg['aria-valuemin'] = this.aria_valuemin;
13583         }
13584         
13585         if(this.aria_valuemax){
13586             cfg['aria-valuemax'] = this.aria_valuemax;
13587         }
13588         
13589         if(this.label && !this.sr_only){
13590             cfg.html = this.label;
13591         }
13592         
13593         if(this.panel){
13594             cfg.cls += ' progress-bar-' + this.panel;
13595         }
13596         
13597         return cfg;
13598     },
13599     
13600     update : function(aria_valuenow)
13601     {
13602         this.aria_valuenow = aria_valuenow;
13603         
13604         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13605     }
13606    
13607 });
13608
13609  
13610
13611  /*
13612  * - LGPL
13613  *
13614  * TabPanel
13615  * 
13616  */
13617
13618 /**
13619  * @class Roo.bootstrap.TabPanel
13620  * @extends Roo.bootstrap.Component
13621  * Bootstrap TabPanel class
13622  * @cfg {Boolean} active panel active
13623  * @cfg {String} html panel content
13624  * @cfg {String} tabId tab relate id
13625  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
13626  * 
13627  * 
13628  * @constructor
13629  * Create a new TabPanel
13630  * @param {Object} config The config object
13631  */
13632
13633 Roo.bootstrap.TabPanel = function(config){
13634     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
13635      this.addEvents({
13636         /**
13637              * @event changed
13638              * Fires when the active status changes
13639              * @param {Roo.bootstrap.TabPanel} this
13640              * @param {Boolean} state the new state
13641             
13642          */
13643         'changed': true
13644      });
13645 };
13646
13647 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
13648     
13649     active: false,
13650     html: false,
13651     tabId: false,
13652     navId : false,
13653     
13654     getAutoCreate : function(){
13655         var cfg = {
13656             tag: 'div',
13657             cls: 'tab-pane',
13658             html: this.html || ''
13659         };
13660         
13661         if(this.active){
13662             cfg.cls += ' active';
13663         }
13664         
13665         if(this.tabId){
13666             cfg.tabId = this.tabId;
13667         }
13668         
13669         return cfg;
13670     },
13671     onRender : function(ct, position)
13672     {
13673        // Roo.log("Call onRender: " + this.xtype);
13674         
13675         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
13676         
13677         if (this.navId && this.tabId) {
13678             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
13679             if (!item) {
13680                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
13681             } else {
13682                 item.on('changed', function(item, state) {
13683                     this.setActive(state);
13684                 }, this);
13685             }
13686         }
13687         
13688     },
13689     setActive: function(state)
13690     {
13691         Roo.log("panel - set active " + this.tabId + "=" + state);
13692         
13693         this.active = state;
13694         if (!state) {
13695             this.el.removeClass('active');
13696             
13697         } else  if (!this.el.hasClass('active')) {
13698             this.el.addClass('active');
13699         }
13700         this.fireEvent('changed', this, state);
13701     }
13702     
13703     
13704 });
13705  
13706
13707  
13708
13709  /*
13710  * - LGPL
13711  *
13712  * DateField
13713  * 
13714  */
13715
13716 /**
13717  * @class Roo.bootstrap.DateField
13718  * @extends Roo.bootstrap.Input
13719  * Bootstrap DateField class
13720  * @cfg {Number} weekStart default 0
13721  * @cfg {Number} weekStart default 0
13722  * @cfg {Number} viewMode default empty, (months|years)
13723  * @cfg {Number} minViewMode default empty, (months|years)
13724  * @cfg {Number} startDate default -Infinity
13725  * @cfg {Number} endDate default Infinity
13726  * @cfg {Boolean} todayHighlight default false
13727  * @cfg {Boolean} todayBtn default false
13728  * @cfg {Boolean} calendarWeeks default false
13729  * @cfg {Object} daysOfWeekDisabled default empty
13730  * 
13731  * @cfg {Boolean} keyboardNavigation default true
13732  * @cfg {String} language default en
13733  * 
13734  * @constructor
13735  * Create a new DateField
13736  * @param {Object} config The config object
13737  */
13738
13739 Roo.bootstrap.DateField = function(config){
13740     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
13741      this.addEvents({
13742             /**
13743              * @event show
13744              * Fires when this field show.
13745              * @param {Roo.bootstrap.DateField} this
13746              * @param {Mixed} date The date value
13747              */
13748             show : true,
13749             /**
13750              * @event show
13751              * Fires when this field hide.
13752              * @param {Roo.bootstrap.DateField} this
13753              * @param {Mixed} date The date value
13754              */
13755             hide : true,
13756             /**
13757              * @event select
13758              * Fires when select a date.
13759              * @param {Roo.bootstrap.DateField} this
13760              * @param {Mixed} date The date value
13761              */
13762             select : true
13763         });
13764 };
13765
13766 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
13767     
13768     /**
13769      * @cfg {String} format
13770      * The default date format string which can be overriden for localization support.  The format must be
13771      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
13772      */
13773     format : "m/d/y",
13774     /**
13775      * @cfg {String} altFormats
13776      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
13777      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
13778      */
13779     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
13780     
13781     weekStart : 0,
13782     
13783     viewMode : '',
13784     
13785     minViewMode : '',
13786     
13787     todayHighlight : false,
13788     
13789     todayBtn: false,
13790     
13791     language: 'en',
13792     
13793     keyboardNavigation: true,
13794     
13795     calendarWeeks: false,
13796     
13797     startDate: -Infinity,
13798     
13799     endDate: Infinity,
13800     
13801     daysOfWeekDisabled: [],
13802     
13803     _events: [],
13804     
13805     UTCDate: function()
13806     {
13807         return new Date(Date.UTC.apply(Date, arguments));
13808     },
13809     
13810     UTCToday: function()
13811     {
13812         var today = new Date();
13813         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
13814     },
13815     
13816     getDate: function() {
13817             var d = this.getUTCDate();
13818             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
13819     },
13820     
13821     getUTCDate: function() {
13822             return this.date;
13823     },
13824     
13825     setDate: function(d) {
13826             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
13827     },
13828     
13829     setUTCDate: function(d) {
13830             this.date = d;
13831             this.setValue(this.formatDate(this.date));
13832     },
13833         
13834     onRender: function(ct, position)
13835     {
13836         
13837         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
13838         
13839         this.language = this.language || 'en';
13840         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
13841         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
13842         
13843         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
13844         this.format = this.format || 'm/d/y';
13845         this.isInline = false;
13846         this.isInput = true;
13847         this.component = this.el.select('.add-on', true).first() || false;
13848         this.component = (this.component && this.component.length === 0) ? false : this.component;
13849         this.hasInput = this.component && this.inputEL().length;
13850         
13851         if (typeof(this.minViewMode === 'string')) {
13852             switch (this.minViewMode) {
13853                 case 'months':
13854                     this.minViewMode = 1;
13855                     break;
13856                 case 'years':
13857                     this.minViewMode = 2;
13858                     break;
13859                 default:
13860                     this.minViewMode = 0;
13861                     break;
13862             }
13863         }
13864         
13865         if (typeof(this.viewMode === 'string')) {
13866             switch (this.viewMode) {
13867                 case 'months':
13868                     this.viewMode = 1;
13869                     break;
13870                 case 'years':
13871                     this.viewMode = 2;
13872                     break;
13873                 default:
13874                     this.viewMode = 0;
13875                     break;
13876             }
13877         }
13878                 
13879         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
13880         
13881         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13882         
13883         this.picker().on('mousedown', this.onMousedown, this);
13884         this.picker().on('click', this.onClick, this);
13885         
13886         this.picker().addClass('datepicker-dropdown');
13887         
13888         this.startViewMode = this.viewMode;
13889         
13890         
13891         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
13892             if(!this.calendarWeeks){
13893                 v.remove();
13894                 return;
13895             };
13896             
13897             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
13898             v.attr('colspan', function(i, val){
13899                 return parseInt(val) + 1;
13900             });
13901         })
13902                         
13903         
13904         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
13905         
13906         this.setStartDate(this.startDate);
13907         this.setEndDate(this.endDate);
13908         
13909         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
13910         
13911         this.fillDow();
13912         this.fillMonths();
13913         this.update();
13914         this.showMode();
13915         
13916         if(this.isInline) {
13917             this.show();
13918         }
13919     },
13920     
13921     picker : function()
13922     {
13923         return this.el.select('.datepicker', true).first();
13924     },
13925     
13926     fillDow: function()
13927     {
13928         var dowCnt = this.weekStart;
13929         
13930         var dow = {
13931             tag: 'tr',
13932             cn: [
13933                 
13934             ]
13935         };
13936         
13937         if(this.calendarWeeks){
13938             dow.cn.push({
13939                 tag: 'th',
13940                 cls: 'cw',
13941                 html: '&nbsp;'
13942             })
13943         }
13944         
13945         while (dowCnt < this.weekStart + 7) {
13946             dow.cn.push({
13947                 tag: 'th',
13948                 cls: 'dow',
13949                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
13950             });
13951         }
13952         
13953         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
13954     },
13955     
13956     fillMonths: function()
13957     {    
13958         var i = 0
13959         var months = this.picker().select('>.datepicker-months td', true).first();
13960         
13961         months.dom.innerHTML = '';
13962         
13963         while (i < 12) {
13964             var month = {
13965                 tag: 'span',
13966                 cls: 'month',
13967                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
13968             }
13969             
13970             months.createChild(month);
13971         }
13972         
13973     },
13974     
13975     update: function()
13976     {
13977         
13978         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
13979         
13980         if (this.date < this.startDate) {
13981             this.viewDate = new Date(this.startDate);
13982         } else if (this.date > this.endDate) {
13983             this.viewDate = new Date(this.endDate);
13984         } else {
13985             this.viewDate = new Date(this.date);
13986         }
13987         
13988         this.fill();
13989     },
13990     
13991     fill: function() 
13992     {
13993         var d = new Date(this.viewDate),
13994                 year = d.getUTCFullYear(),
13995                 month = d.getUTCMonth(),
13996                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
13997                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
13998                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
13999                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14000                 currentDate = this.date && this.date.valueOf(),
14001                 today = this.UTCToday();
14002         
14003         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14004         
14005 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14006         
14007 //        this.picker.select('>tfoot th.today').
14008 //                                              .text(dates[this.language].today)
14009 //                                              .toggle(this.todayBtn !== false);
14010     
14011         this.updateNavArrows();
14012         this.fillMonths();
14013                                                 
14014         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14015         
14016         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14017          
14018         prevMonth.setUTCDate(day);
14019         
14020         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14021         
14022         var nextMonth = new Date(prevMonth);
14023         
14024         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14025         
14026         nextMonth = nextMonth.valueOf();
14027         
14028         var fillMonths = false;
14029         
14030         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14031         
14032         while(prevMonth.valueOf() < nextMonth) {
14033             var clsName = '';
14034             
14035             if (prevMonth.getUTCDay() === this.weekStart) {
14036                 if(fillMonths){
14037                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14038                 }
14039                     
14040                 fillMonths = {
14041                     tag: 'tr',
14042                     cn: []
14043                 };
14044                 
14045                 if(this.calendarWeeks){
14046                     // ISO 8601: First week contains first thursday.
14047                     // ISO also states week starts on Monday, but we can be more abstract here.
14048                     var
14049                     // Start of current week: based on weekstart/current date
14050                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14051                     // Thursday of this week
14052                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14053                     // First Thursday of year, year from thursday
14054                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14055                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14056                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14057                     
14058                     fillMonths.cn.push({
14059                         tag: 'td',
14060                         cls: 'cw',
14061                         html: calWeek
14062                     });
14063                 }
14064             }
14065             
14066             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14067                 clsName += ' old';
14068             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14069                 clsName += ' new';
14070             }
14071             if (this.todayHighlight &&
14072                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14073                 prevMonth.getUTCMonth() == today.getMonth() &&
14074                 prevMonth.getUTCDate() == today.getDate()) {
14075                 clsName += ' today';
14076             }
14077             
14078             if (currentDate && prevMonth.valueOf() === currentDate) {
14079                 clsName += ' active';
14080             }
14081             
14082             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14083                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14084                     clsName += ' disabled';
14085             }
14086             
14087             fillMonths.cn.push({
14088                 tag: 'td',
14089                 cls: 'day ' + clsName,
14090                 html: prevMonth.getDate()
14091             })
14092             
14093             prevMonth.setDate(prevMonth.getDate()+1);
14094         }
14095           
14096         var currentYear = this.date && this.date.getUTCFullYear();
14097         var currentMonth = this.date && this.date.getUTCMonth();
14098         
14099         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14100         
14101         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14102             v.removeClass('active');
14103             
14104             if(currentYear === year && k === currentMonth){
14105                 v.addClass('active');
14106             }
14107             
14108             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14109                 v.addClass('disabled');
14110             }
14111             
14112         });
14113         
14114         
14115         year = parseInt(year/10, 10) * 10;
14116         
14117         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14118         
14119         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14120         
14121         year -= 1;
14122         for (var i = -1; i < 11; i++) {
14123             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14124                 tag: 'span',
14125                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14126                 html: year
14127             })
14128             
14129             year += 1;
14130         }
14131     },
14132     
14133     showMode: function(dir) 
14134     {
14135         if (dir) {
14136             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14137         }
14138         Roo.each(this.picker().select('>div',true).elements, function(v){
14139             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14140             v.hide();
14141         });
14142         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14143     },
14144     
14145     place: function()
14146     {
14147         if(this.isInline) return;
14148         
14149         this.picker().removeClass(['bottom', 'top']);
14150         
14151         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14152             /*
14153              * place to the top of element!
14154              *
14155              */
14156             
14157             this.picker().addClass('top');
14158             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14159             
14160             return;
14161         }
14162         
14163         this.picker().addClass('bottom');
14164         
14165         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14166     },
14167     
14168     parseDate : function(value)
14169     {
14170         if(!value || value instanceof Date){
14171             return value;
14172         }
14173         var v = Date.parseDate(value, this.format);
14174         if (!v && this.useIso) {
14175             v = Date.parseDate(value, 'Y-m-d');
14176         }
14177         if(!v && this.altFormats){
14178             if(!this.altFormatsArray){
14179                 this.altFormatsArray = this.altFormats.split("|");
14180             }
14181             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14182                 v = Date.parseDate(value, this.altFormatsArray[i]);
14183             }
14184         }
14185         return v;
14186     },
14187     
14188     formatDate : function(date, fmt)
14189     {
14190         return (!date || !(date instanceof Date)) ?
14191         date : date.dateFormat(fmt || this.format);
14192     },
14193     
14194     onFocus : function()
14195     {
14196         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14197         this.show();
14198     },
14199     
14200     onBlur : function()
14201     {
14202         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14203         
14204         var d = this.inputEl().getValue();
14205         
14206         if(d && d.length){
14207             this.setValue(d);
14208         }
14209                 
14210         this.hide();
14211     },
14212     
14213     show : function()
14214     {
14215         this.picker().show();
14216         this.update();
14217         this.place();
14218         
14219         this.fireEvent('show', this, this.date);
14220     },
14221     
14222     hide : function()
14223     {
14224         if(this.isInline) return;
14225         this.picker().hide();
14226         this.viewMode = this.startViewMode;
14227         this.showMode();
14228         
14229         this.fireEvent('hide', this, this.date);
14230         
14231     },
14232     
14233     onMousedown: function(e)
14234     {
14235         e.stopPropagation();
14236         e.preventDefault();
14237     },
14238     
14239     keyup: function(e)
14240     {
14241         Roo.bootstrap.DateField.superclass.keyup.call(this);
14242         this.update();
14243     },
14244
14245     setValue: function(v)
14246     {
14247         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14248         
14249         var d = new Date(v);
14250         
14251         if(isNaN(d.getTime())){
14252             return;
14253         }
14254         
14255         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14256
14257         this.update();
14258
14259         this.fireEvent('select', this, this.date);
14260         
14261     },
14262     
14263     getValue: function()
14264     {
14265         return this.formatDate(this.date);
14266     },
14267     
14268     fireKey: function(e)
14269     {
14270         if (!this.picker().isVisible()){
14271             if (e.keyCode == 27) // allow escape to hide and re-show picker
14272                 this.show();
14273             return;
14274         }
14275         var dateChanged = false,
14276         dir, day, month,
14277         newDate, newViewDate;
14278         
14279         switch(e.keyCode){
14280             case 27: // escape
14281                 this.hide();
14282                 e.preventDefault();
14283                 break;
14284             case 37: // left
14285             case 39: // right
14286                 if (!this.keyboardNavigation) break;
14287                 dir = e.keyCode == 37 ? -1 : 1;
14288                 
14289                 if (e.ctrlKey){
14290                     newDate = this.moveYear(this.date, dir);
14291                     newViewDate = this.moveYear(this.viewDate, dir);
14292                 } else if (e.shiftKey){
14293                     newDate = this.moveMonth(this.date, dir);
14294                     newViewDate = this.moveMonth(this.viewDate, dir);
14295                 } else {
14296                     newDate = new Date(this.date);
14297                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14298                     newViewDate = new Date(this.viewDate);
14299                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14300                 }
14301                 if (this.dateWithinRange(newDate)){
14302                     this.date = newDate;
14303                     this.viewDate = newViewDate;
14304                     this.setValue(this.formatDate(this.date));
14305 //                    this.update();
14306                     e.preventDefault();
14307                     dateChanged = true;
14308                 }
14309                 break;
14310             case 38: // up
14311             case 40: // down
14312                 if (!this.keyboardNavigation) break;
14313                 dir = e.keyCode == 38 ? -1 : 1;
14314                 if (e.ctrlKey){
14315                     newDate = this.moveYear(this.date, dir);
14316                     newViewDate = this.moveYear(this.viewDate, dir);
14317                 } else if (e.shiftKey){
14318                     newDate = this.moveMonth(this.date, dir);
14319                     newViewDate = this.moveMonth(this.viewDate, dir);
14320                 } else {
14321                     newDate = new Date(this.date);
14322                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14323                     newViewDate = new Date(this.viewDate);
14324                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14325                 }
14326                 if (this.dateWithinRange(newDate)){
14327                     this.date = newDate;
14328                     this.viewDate = newViewDate;
14329                     this.setValue(this.formatDate(this.date));
14330 //                    this.update();
14331                     e.preventDefault();
14332                     dateChanged = true;
14333                 }
14334                 break;
14335             case 13: // enter
14336                 this.setValue(this.formatDate(this.date));
14337                 this.hide();
14338                 e.preventDefault();
14339                 break;
14340             case 9: // tab
14341                 this.setValue(this.formatDate(this.date));
14342                 this.hide();
14343                 break;
14344                 
14345         }
14346     },
14347     
14348     
14349     onClick: function(e) 
14350     {
14351         e.stopPropagation();
14352         e.preventDefault();
14353         
14354         var target = e.getTarget();
14355         
14356         if(target.nodeName.toLowerCase() === 'i'){
14357             target = Roo.get(target).dom.parentNode;
14358         }
14359         
14360         var nodeName = target.nodeName;
14361         var className = target.className;
14362         var html = target.innerHTML;
14363         
14364         switch(nodeName.toLowerCase()) {
14365             case 'th':
14366                 switch(className) {
14367                     case 'switch':
14368                         this.showMode(1);
14369                         break;
14370                     case 'prev':
14371                     case 'next':
14372                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14373                         switch(this.viewMode){
14374                                 case 0:
14375                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14376                                         break;
14377                                 case 1:
14378                                 case 2:
14379                                         this.viewDate = this.moveYear(this.viewDate, dir);
14380                                         break;
14381                         }
14382                         this.fill();
14383                         break;
14384                     case 'today':
14385                         var date = new Date();
14386                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14387 //                        this.fill()
14388                         this.setValue(this.formatDate(this.date));
14389                         
14390                         this.hide();
14391                         break;
14392                 }
14393                 break;
14394             case 'span':
14395                 if (className.indexOf('disabled') === -1) {
14396                     this.viewDate.setUTCDate(1);
14397                     if (className.indexOf('month') !== -1) {
14398                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14399                     } else {
14400                         var year = parseInt(html, 10) || 0;
14401                         this.viewDate.setUTCFullYear(year);
14402                         
14403                     }
14404                     this.showMode(-1);
14405                     this.fill();
14406                 }
14407                 break;
14408                 
14409             case 'td':
14410                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14411                     var day = parseInt(html, 10) || 1;
14412                     var year = this.viewDate.getUTCFullYear(),
14413                         month = this.viewDate.getUTCMonth();
14414
14415                     if (className.indexOf('old') !== -1) {
14416                         if(month === 0 ){
14417                             month = 11;
14418                             year -= 1;
14419                         }else{
14420                             month -= 1;
14421                         }
14422                     } else if (className.indexOf('new') !== -1) {
14423                         if (month == 11) {
14424                             month = 0;
14425                             year += 1;
14426                         } else {
14427                             month += 1;
14428                         }
14429                     }
14430                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14431                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14432 //                    this.fill();
14433                     this.setValue(this.formatDate(this.date));
14434                     this.hide();
14435                 }
14436                 break;
14437         }
14438     },
14439     
14440     setStartDate: function(startDate)
14441     {
14442         this.startDate = startDate || -Infinity;
14443         if (this.startDate !== -Infinity) {
14444             this.startDate = this.parseDate(this.startDate);
14445         }
14446         this.update();
14447         this.updateNavArrows();
14448     },
14449
14450     setEndDate: function(endDate)
14451     {
14452         this.endDate = endDate || Infinity;
14453         if (this.endDate !== Infinity) {
14454             this.endDate = this.parseDate(this.endDate);
14455         }
14456         this.update();
14457         this.updateNavArrows();
14458     },
14459     
14460     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
14461     {
14462         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
14463         if (typeof(this.daysOfWeekDisabled) !== 'object') {
14464             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
14465         }
14466         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
14467             return parseInt(d, 10);
14468         });
14469         this.update();
14470         this.updateNavArrows();
14471     },
14472     
14473     updateNavArrows: function() 
14474     {
14475         var d = new Date(this.viewDate),
14476         year = d.getUTCFullYear(),
14477         month = d.getUTCMonth();
14478         
14479         Roo.each(this.picker().select('.prev', true).elements, function(v){
14480             v.show();
14481             switch (this.viewMode) {
14482                 case 0:
14483
14484                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
14485                         v.hide();
14486                     }
14487                     break;
14488                 case 1:
14489                 case 2:
14490                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
14491                         v.hide();
14492                     }
14493                     break;
14494             }
14495         });
14496         
14497         Roo.each(this.picker().select('.next', true).elements, function(v){
14498             v.show();
14499             switch (this.viewMode) {
14500                 case 0:
14501
14502                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
14503                         v.hide();
14504                     }
14505                     break;
14506                 case 1:
14507                 case 2:
14508                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
14509                         v.hide();
14510                     }
14511                     break;
14512             }
14513         })
14514     },
14515     
14516     moveMonth: function(date, dir)
14517     {
14518         if (!dir) return date;
14519         var new_date = new Date(date.valueOf()),
14520         day = new_date.getUTCDate(),
14521         month = new_date.getUTCMonth(),
14522         mag = Math.abs(dir),
14523         new_month, test;
14524         dir = dir > 0 ? 1 : -1;
14525         if (mag == 1){
14526             test = dir == -1
14527             // If going back one month, make sure month is not current month
14528             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
14529             ? function(){
14530                 return new_date.getUTCMonth() == month;
14531             }
14532             // If going forward one month, make sure month is as expected
14533             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
14534             : function(){
14535                 return new_date.getUTCMonth() != new_month;
14536             };
14537             new_month = month + dir;
14538             new_date.setUTCMonth(new_month);
14539             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
14540             if (new_month < 0 || new_month > 11)
14541                 new_month = (new_month + 12) % 12;
14542         } else {
14543             // For magnitudes >1, move one month at a time...
14544             for (var i=0; i<mag; i++)
14545                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
14546                 new_date = this.moveMonth(new_date, dir);
14547             // ...then reset the day, keeping it in the new month
14548             new_month = new_date.getUTCMonth();
14549             new_date.setUTCDate(day);
14550             test = function(){
14551                 return new_month != new_date.getUTCMonth();
14552             };
14553         }
14554         // Common date-resetting loop -- if date is beyond end of month, make it
14555         // end of month
14556         while (test()){
14557             new_date.setUTCDate(--day);
14558             new_date.setUTCMonth(new_month);
14559         }
14560         return new_date;
14561     },
14562
14563     moveYear: function(date, dir)
14564     {
14565         return this.moveMonth(date, dir*12);
14566     },
14567
14568     dateWithinRange: function(date)
14569     {
14570         return date >= this.startDate && date <= this.endDate;
14571     },
14572
14573     
14574     remove: function() 
14575     {
14576         this.picker().remove();
14577     }
14578    
14579 });
14580
14581 Roo.apply(Roo.bootstrap.DateField,  {
14582     
14583     head : {
14584         tag: 'thead',
14585         cn: [
14586         {
14587             tag: 'tr',
14588             cn: [
14589             {
14590                 tag: 'th',
14591                 cls: 'prev',
14592                 html: '<i class="fa fa-arrow-left"/>'
14593             },
14594             {
14595                 tag: 'th',
14596                 cls: 'switch',
14597                 colspan: '5'
14598             },
14599             {
14600                 tag: 'th',
14601                 cls: 'next',
14602                 html: '<i class="fa fa-arrow-right"/>'
14603             }
14604
14605             ]
14606         }
14607         ]
14608     },
14609     
14610     content : {
14611         tag: 'tbody',
14612         cn: [
14613         {
14614             tag: 'tr',
14615             cn: [
14616             {
14617                 tag: 'td',
14618                 colspan: '7'
14619             }
14620             ]
14621         }
14622         ]
14623     },
14624     
14625     footer : {
14626         tag: 'tfoot',
14627         cn: [
14628         {
14629             tag: 'tr',
14630             cn: [
14631             {
14632                 tag: 'th',
14633                 colspan: '7',
14634                 cls: 'today'
14635             }
14636                     
14637             ]
14638         }
14639         ]
14640     },
14641     
14642     dates:{
14643         en: {
14644             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
14645             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
14646             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
14647             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
14648             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
14649             today: "Today"
14650         }
14651     },
14652     
14653     modes: [
14654     {
14655         clsName: 'days',
14656         navFnc: 'Month',
14657         navStep: 1
14658     },
14659     {
14660         clsName: 'months',
14661         navFnc: 'FullYear',
14662         navStep: 1
14663     },
14664     {
14665         clsName: 'years',
14666         navFnc: 'FullYear',
14667         navStep: 10
14668     }]
14669 });
14670
14671 Roo.apply(Roo.bootstrap.DateField,  {
14672   
14673     template : {
14674         tag: 'div',
14675         cls: 'datepicker dropdown-menu',
14676         cn: [
14677         {
14678             tag: 'div',
14679             cls: 'datepicker-days',
14680             cn: [
14681             {
14682                 tag: 'table',
14683                 cls: 'table-condensed',
14684                 cn:[
14685                 Roo.bootstrap.DateField.head,
14686                 {
14687                     tag: 'tbody'
14688                 },
14689                 Roo.bootstrap.DateField.footer
14690                 ]
14691             }
14692             ]
14693         },
14694         {
14695             tag: 'div',
14696             cls: 'datepicker-months',
14697             cn: [
14698             {
14699                 tag: 'table',
14700                 cls: 'table-condensed',
14701                 cn:[
14702                 Roo.bootstrap.DateField.head,
14703                 Roo.bootstrap.DateField.content,
14704                 Roo.bootstrap.DateField.footer
14705                 ]
14706             }
14707             ]
14708         },
14709         {
14710             tag: 'div',
14711             cls: 'datepicker-years',
14712             cn: [
14713             {
14714                 tag: 'table',
14715                 cls: 'table-condensed',
14716                 cn:[
14717                 Roo.bootstrap.DateField.head,
14718                 Roo.bootstrap.DateField.content,
14719                 Roo.bootstrap.DateField.footer
14720                 ]
14721             }
14722             ]
14723         }
14724         ]
14725     }
14726 });
14727
14728  
14729
14730  /*
14731  * - LGPL
14732  *
14733  * TimeField
14734  * 
14735  */
14736
14737 /**
14738  * @class Roo.bootstrap.TimeField
14739  * @extends Roo.bootstrap.Input
14740  * Bootstrap DateField class
14741  * 
14742  * 
14743  * @constructor
14744  * Create a new TimeField
14745  * @param {Object} config The config object
14746  */
14747
14748 Roo.bootstrap.TimeField = function(config){
14749     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
14750     this.addEvents({
14751             /**
14752              * @event show
14753              * Fires when this field show.
14754              * @param {Roo.bootstrap.DateField} this
14755              * @param {Mixed} date The date value
14756              */
14757             show : true,
14758             /**
14759              * @event show
14760              * Fires when this field hide.
14761              * @param {Roo.bootstrap.DateField} this
14762              * @param {Mixed} date The date value
14763              */
14764             hide : true,
14765             /**
14766              * @event select
14767              * Fires when select a date.
14768              * @param {Roo.bootstrap.DateField} this
14769              * @param {Mixed} date The date value
14770              */
14771             select : true
14772         });
14773 };
14774
14775 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
14776     
14777     /**
14778      * @cfg {String} format
14779      * The default time format string which can be overriden for localization support.  The format must be
14780      * valid according to {@link Date#parseDate} (defaults to 'H:i').
14781      */
14782     format : "H:i",
14783        
14784     onRender: function(ct, position)
14785     {
14786         
14787         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
14788                 
14789         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
14790         
14791         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14792         
14793         this.pop = this.picker().select('>.datepicker-time',true).first();
14794         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
14795         
14796         this.picker().on('mousedown', this.onMousedown, this);
14797         this.picker().on('click', this.onClick, this);
14798         
14799         this.picker().addClass('datepicker-dropdown');
14800     
14801         this.fillTime();
14802         this.update();
14803             
14804         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
14805         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
14806         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
14807         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
14808         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
14809         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
14810
14811     },
14812     
14813     fireKey: function(e){
14814         if (!this.picker().isVisible()){
14815             if (e.keyCode == 27) // allow escape to hide and re-show picker
14816                 this.show();
14817             return;
14818         }
14819
14820         e.preventDefault();
14821         
14822         switch(e.keyCode){
14823             case 27: // escape
14824                 this.hide();
14825                 break;
14826             case 37: // left
14827             case 39: // right
14828                 this.onTogglePeriod();
14829                 break;
14830             case 38: // up
14831                 this.onIncrementMinutes();
14832                 break;
14833             case 40: // down
14834                 this.onDecrementMinutes();
14835                 break;
14836             case 13: // enter
14837             case 9: // tab
14838                 this.setTime();
14839                 break;
14840         }
14841     },
14842     
14843     onClick: function(e) {
14844         e.stopPropagation();
14845         e.preventDefault();
14846     },
14847     
14848     picker : function()
14849     {
14850         return this.el.select('.datepicker', true).first();
14851     },
14852     
14853     fillTime: function()
14854     {    
14855         var time = this.pop.select('tbody', true).first();
14856         
14857         time.dom.innerHTML = '';
14858         
14859         time.createChild({
14860             tag: 'tr',
14861             cn: [
14862                 {
14863                     tag: 'td',
14864                     cn: [
14865                         {
14866                             tag: 'a',
14867                             href: '#',
14868                             cls: 'btn',
14869                             cn: [
14870                                 {
14871                                     tag: 'span',
14872                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
14873                                 }
14874                             ]
14875                         } 
14876                     ]
14877                 },
14878                 {
14879                     tag: 'td',
14880                     cls: 'separator'
14881                 },
14882                 {
14883                     tag: 'td',
14884                     cn: [
14885                         {
14886                             tag: 'a',
14887                             href: '#',
14888                             cls: 'btn',
14889                             cn: [
14890                                 {
14891                                     tag: 'span',
14892                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
14893                                 }
14894                             ]
14895                         }
14896                     ]
14897                 },
14898                 {
14899                     tag: 'td',
14900                     cls: 'separator'
14901                 }
14902             ]
14903         });
14904         
14905         time.createChild({
14906             tag: 'tr',
14907             cn: [
14908                 {
14909                     tag: 'td',
14910                     cn: [
14911                         {
14912                             tag: 'span',
14913                             cls: 'timepicker-hour',
14914                             html: '00'
14915                         }  
14916                     ]
14917                 },
14918                 {
14919                     tag: 'td',
14920                     cls: 'separator',
14921                     html: ':'
14922                 },
14923                 {
14924                     tag: 'td',
14925                     cn: [
14926                         {
14927                             tag: 'span',
14928                             cls: 'timepicker-minute',
14929                             html: '00'
14930                         }  
14931                     ]
14932                 },
14933                 {
14934                     tag: 'td',
14935                     cls: 'separator'
14936                 },
14937                 {
14938                     tag: 'td',
14939                     cn: [
14940                         {
14941                             tag: 'button',
14942                             type: 'button',
14943                             cls: 'btn btn-primary period',
14944                             html: 'AM'
14945                             
14946                         }
14947                     ]
14948                 }
14949             ]
14950         });
14951         
14952         time.createChild({
14953             tag: 'tr',
14954             cn: [
14955                 {
14956                     tag: 'td',
14957                     cn: [
14958                         {
14959                             tag: 'a',
14960                             href: '#',
14961                             cls: 'btn',
14962                             cn: [
14963                                 {
14964                                     tag: 'span',
14965                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
14966                                 }
14967                             ]
14968                         }
14969                     ]
14970                 },
14971                 {
14972                     tag: 'td',
14973                     cls: 'separator'
14974                 },
14975                 {
14976                     tag: 'td',
14977                     cn: [
14978                         {
14979                             tag: 'a',
14980                             href: '#',
14981                             cls: 'btn',
14982                             cn: [
14983                                 {
14984                                     tag: 'span',
14985                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
14986                                 }
14987                             ]
14988                         }
14989                     ]
14990                 },
14991                 {
14992                     tag: 'td',
14993                     cls: 'separator'
14994                 }
14995             ]
14996         });
14997         
14998     },
14999     
15000     update: function()
15001     {
15002         
15003         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15004         
15005         this.fill();
15006     },
15007     
15008     fill: function() 
15009     {
15010         var hours = this.time.getHours();
15011         var minutes = this.time.getMinutes();
15012         var period = 'AM';
15013         
15014         if(hours > 11){
15015             period = 'PM';
15016         }
15017         
15018         if(hours == 0){
15019             hours = 12;
15020         }
15021         
15022         
15023         if(hours > 12){
15024             hours = hours - 12;
15025         }
15026         
15027         if(hours < 10){
15028             hours = '0' + hours;
15029         }
15030         
15031         if(minutes < 10){
15032             minutes = '0' + minutes;
15033         }
15034         
15035         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15036         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15037         this.pop.select('button', true).first().dom.innerHTML = period;
15038         
15039     },
15040     
15041     place: function()
15042     {   
15043         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15044         
15045         var cls = ['bottom'];
15046         
15047         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15048             cls.pop();
15049             cls.push('top');
15050         }
15051         
15052         cls.push('right');
15053         
15054         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15055             cls.pop();
15056             cls.push('left');
15057         }
15058         
15059         this.picker().addClass(cls.join('-'));
15060         
15061         var _this = this;
15062         
15063         Roo.each(cls, function(c){
15064             if(c == 'bottom'){
15065                 _this.picker().setTop(_this.inputEl().getHeight());
15066                 return;
15067             }
15068             if(c == 'top'){
15069                 _this.picker().setTop(0 - _this.picker().getHeight());
15070                 return;
15071             }
15072             
15073             if(c == 'left'){
15074                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15075                 return;
15076             }
15077             if(c == 'right'){
15078                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15079                 return;
15080             }
15081         });
15082         
15083     },
15084   
15085     onFocus : function()
15086     {
15087         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15088         this.show();
15089     },
15090     
15091     onBlur : function()
15092     {
15093         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15094         this.hide();
15095     },
15096     
15097     show : function()
15098     {
15099         this.picker().show();
15100         this.pop.show();
15101         this.update();
15102         this.place();
15103         
15104         this.fireEvent('show', this, this.date);
15105     },
15106     
15107     hide : function()
15108     {
15109         this.picker().hide();
15110         this.pop.hide();
15111         
15112         this.fireEvent('hide', this, this.date);
15113     },
15114     
15115     setTime : function()
15116     {
15117         this.hide();
15118         this.setValue(this.time.format(this.format));
15119         
15120         this.fireEvent('select', this, this.date);
15121         
15122         
15123     },
15124     
15125     onMousedown: function(e){
15126         e.stopPropagation();
15127         e.preventDefault();
15128     },
15129     
15130     onIncrementHours: function()
15131     {
15132         Roo.log('onIncrementHours');
15133         this.time = this.time.add(Date.HOUR, 1);
15134         this.update();
15135         
15136     },
15137     
15138     onDecrementHours: function()
15139     {
15140         Roo.log('onDecrementHours');
15141         this.time = this.time.add(Date.HOUR, -1);
15142         this.update();
15143     },
15144     
15145     onIncrementMinutes: function()
15146     {
15147         Roo.log('onIncrementMinutes');
15148         this.time = this.time.add(Date.MINUTE, 1);
15149         this.update();
15150     },
15151     
15152     onDecrementMinutes: function()
15153     {
15154         Roo.log('onDecrementMinutes');
15155         this.time = this.time.add(Date.MINUTE, -1);
15156         this.update();
15157     },
15158     
15159     onTogglePeriod: function()
15160     {
15161         Roo.log('onTogglePeriod');
15162         this.time = this.time.add(Date.HOUR, 12);
15163         this.update();
15164     }
15165     
15166    
15167 });
15168
15169 Roo.apply(Roo.bootstrap.TimeField,  {
15170     
15171     content : {
15172         tag: 'tbody',
15173         cn: [
15174             {
15175                 tag: 'tr',
15176                 cn: [
15177                 {
15178                     tag: 'td',
15179                     colspan: '7'
15180                 }
15181                 ]
15182             }
15183         ]
15184     },
15185     
15186     footer : {
15187         tag: 'tfoot',
15188         cn: [
15189             {
15190                 tag: 'tr',
15191                 cn: [
15192                 {
15193                     tag: 'th',
15194                     colspan: '7',
15195                     cls: '',
15196                     cn: [
15197                         {
15198                             tag: 'button',
15199                             cls: 'btn btn-info ok',
15200                             html: 'OK'
15201                         }
15202                     ]
15203                 }
15204
15205                 ]
15206             }
15207         ]
15208     }
15209 });
15210
15211 Roo.apply(Roo.bootstrap.TimeField,  {
15212   
15213     template : {
15214         tag: 'div',
15215         cls: 'datepicker dropdown-menu',
15216         cn: [
15217             {
15218                 tag: 'div',
15219                 cls: 'datepicker-time',
15220                 cn: [
15221                 {
15222                     tag: 'table',
15223                     cls: 'table-condensed',
15224                     cn:[
15225                     Roo.bootstrap.TimeField.content,
15226                     Roo.bootstrap.TimeField.footer
15227                     ]
15228                 }
15229                 ]
15230             }
15231         ]
15232     }
15233 });
15234
15235  
15236
15237  /*
15238  * - LGPL
15239  *
15240  * CheckBox
15241  * 
15242  */
15243
15244 /**
15245  * @class Roo.bootstrap.CheckBox
15246  * @extends Roo.bootstrap.Input
15247  * Bootstrap CheckBox class
15248  * 
15249  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15250  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15251  * @cfg {String} boxLabel The text that appears beside the checkbox
15252  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15253  * @cfg {Boolean} checked initnal the element
15254  * 
15255  * 
15256  * @constructor
15257  * Create a new CheckBox
15258  * @param {Object} config The config object
15259  */
15260
15261 Roo.bootstrap.CheckBox = function(config){
15262     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15263    
15264         this.addEvents({
15265             /**
15266             * @event check
15267             * Fires when the element is checked or unchecked.
15268             * @param {Roo.bootstrap.CheckBox} this This input
15269             * @param {Boolean} checked The new checked value
15270             */
15271            check : true
15272         });
15273 };
15274
15275 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15276     
15277     inputType: 'checkbox',
15278     inputValue: 1,
15279     valueOff: 0,
15280     boxLabel: false,
15281     checked: false,
15282     weight : false,
15283     
15284     getAutoCreate : function()
15285     {
15286         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15287         
15288         var id = Roo.id();
15289         
15290         var cfg = {};
15291         
15292         cfg.cls = 'form-group checkbox' //input-group
15293         
15294         
15295         
15296         
15297         var input =  {
15298             tag: 'input',
15299             id : id,
15300             type : this.inputType,
15301             value : (!this.checked) ? this.valueOff : this.inputValue,
15302             cls : 'roo-checkbox', //'form-box',
15303             placeholder : this.placeholder || ''
15304             
15305         };
15306         
15307         if (this.weight) { // Validity check?
15308             cfg.cls += " checkbox-" + this.weight;
15309         }
15310         
15311         if (this.disabled) {
15312             input.disabled=true;
15313         }
15314         
15315         if(this.checked){
15316             input.checked = this.checked;
15317         }
15318         
15319         if (this.name) {
15320             input.name = this.name;
15321         }
15322         
15323         if (this.size) {
15324             input.cls += ' input-' + this.size;
15325         }
15326         
15327         var settings=this;
15328         ['xs','sm','md','lg'].map(function(size){
15329             if (settings[size]) {
15330                 cfg.cls += ' col-' + size + '-' + settings[size];
15331             }
15332         });
15333         
15334        
15335         
15336         var inputblock = input;
15337         
15338         
15339         
15340         
15341         if (this.before || this.after) {
15342             
15343             inputblock = {
15344                 cls : 'input-group',
15345                 cn :  [] 
15346             };
15347             if (this.before) {
15348                 inputblock.cn.push({
15349                     tag :'span',
15350                     cls : 'input-group-addon',
15351                     html : this.before
15352                 });
15353             }
15354             inputblock.cn.push(input);
15355             if (this.after) {
15356                 inputblock.cn.push({
15357                     tag :'span',
15358                     cls : 'input-group-addon',
15359                     html : this.after
15360                 });
15361             }
15362             
15363         };
15364         
15365         if (align ==='left' && this.fieldLabel.length) {
15366                 Roo.log("left and has label");
15367                 cfg.cn = [
15368                     
15369                     {
15370                         tag: 'label',
15371                         'for' :  id,
15372                         cls : 'control-label col-md-' + this.labelWidth,
15373                         html : this.fieldLabel
15374                         
15375                     },
15376                     {
15377                         cls : "col-md-" + (12 - this.labelWidth), 
15378                         cn: [
15379                             inputblock
15380                         ]
15381                     }
15382                     
15383                 ];
15384         } else if ( this.fieldLabel.length) {
15385                 Roo.log(" label");
15386                 cfg.cn = [
15387                    
15388                     {
15389                         tag: this.boxLabel ? 'span' : 'label',
15390                         'for': id,
15391                         cls: 'control-label box-input-label',
15392                         //cls : 'input-group-addon',
15393                         html : this.fieldLabel
15394                         
15395                     },
15396                     
15397                     inputblock
15398                     
15399                 ];
15400
15401         } else {
15402             
15403                 Roo.log(" no label && no align");
15404                 cfg.cn = [  inputblock ] ;
15405                 
15406                 
15407         };
15408          if(this.boxLabel){
15409             cfg.cn.push( {
15410                 tag: 'label',
15411                 'for': id,
15412                 cls: 'box-label',
15413                 html: this.boxLabel
15414                 
15415             });
15416         }
15417         
15418         
15419        
15420         return cfg;
15421         
15422     },
15423     
15424     /**
15425      * return the real input element.
15426      */
15427     inputEl: function ()
15428     {
15429         return this.el.select('input.roo-checkbox',true).first();
15430     },
15431     
15432     label: function()
15433     {
15434         return this.el.select('label.control-label',true).first();
15435     },
15436     
15437     initEvents : function()
15438     {
15439 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15440         
15441         this.inputEl().on('click', this.onClick,  this);
15442         
15443     },
15444     
15445     onClick : function()
15446     {   
15447         this.setChecked(!this.checked);
15448     },
15449     
15450     setChecked : function(state,suppressEvent)
15451     {
15452         this.checked = state;
15453         
15454         this.inputEl().dom.checked = state;
15455         
15456         if(suppressEvent !== true){
15457             this.fireEvent('check', this, state);
15458         }
15459         
15460         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15461         
15462     },
15463     
15464     setValue : function(v,suppressEvent)
15465     {
15466         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
15467     }
15468     
15469 });
15470
15471  
15472 /*
15473  * - LGPL
15474  *
15475  * Radio
15476  * 
15477  */
15478
15479 /**
15480  * @class Roo.bootstrap.Radio
15481  * @extends Roo.bootstrap.CheckBox
15482  * Bootstrap Radio class
15483
15484  * @constructor
15485  * Create a new Radio
15486  * @param {Object} config The config object
15487  */
15488
15489 Roo.bootstrap.Radio = function(config){
15490     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
15491    
15492 };
15493
15494 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
15495     
15496     inputType: 'radio',
15497     inputValue: '',
15498     valueOff: '',
15499     
15500     getAutoCreate : function()
15501     {
15502         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15503         
15504         var id = Roo.id();
15505         
15506         var cfg = {};
15507         
15508         cfg.cls = 'form-group radio' //input-group
15509         
15510         var input =  {
15511             tag: 'input',
15512             id : id,
15513             type : this.inputType,
15514             value : (!this.checked) ? this.valueOff : this.inputValue,
15515             cls : 'roo-radio',
15516             placeholder : this.placeholder || ''
15517             
15518         };
15519           if (this.weight) { // Validity check?
15520             cfg.cls += " radio-" + this.weight;
15521         }
15522         if (this.disabled) {
15523             input.disabled=true;
15524         }
15525         
15526         if(this.checked){
15527             input.checked = this.checked;
15528         }
15529         
15530         if (this.name) {
15531             input.name = this.name;
15532         }
15533         
15534         if (this.size) {
15535             input.cls += ' input-' + this.size;
15536         }
15537         
15538         var settings=this;
15539         ['xs','sm','md','lg'].map(function(size){
15540             if (settings[size]) {
15541                 cfg.cls += ' col-' + size + '-' + settings[size];
15542             }
15543         });
15544         
15545         var inputblock = input;
15546         
15547         if (this.before || this.after) {
15548             
15549             inputblock = {
15550                 cls : 'input-group',
15551                 cn :  [] 
15552             };
15553             if (this.before) {
15554                 inputblock.cn.push({
15555                     tag :'span',
15556                     cls : 'input-group-addon',
15557                     html : this.before
15558                 });
15559             }
15560             inputblock.cn.push(input);
15561             if (this.after) {
15562                 inputblock.cn.push({
15563                     tag :'span',
15564                     cls : 'input-group-addon',
15565                     html : this.after
15566                 });
15567             }
15568             
15569         };
15570         
15571         if (align ==='left' && this.fieldLabel.length) {
15572                 Roo.log("left and has label");
15573                 cfg.cn = [
15574                     
15575                     {
15576                         tag: 'label',
15577                         'for' :  id,
15578                         cls : 'control-label col-md-' + this.labelWidth,
15579                         html : this.fieldLabel
15580                         
15581                     },
15582                     {
15583                         cls : "col-md-" + (12 - this.labelWidth), 
15584                         cn: [
15585                             inputblock
15586                         ]
15587                     }
15588                     
15589                 ];
15590         } else if ( this.fieldLabel.length) {
15591                 Roo.log(" label");
15592                  cfg.cn = [
15593                    
15594                     {
15595                         tag: 'label',
15596                         'for': id,
15597                         cls: 'control-label box-input-label',
15598                         //cls : 'input-group-addon',
15599                         html : this.fieldLabel
15600                         
15601                     },
15602                     
15603                     inputblock
15604                     
15605                 ];
15606
15607         } else {
15608             
15609                    Roo.log(" no label && no align");
15610                 cfg.cn = [
15611                     
15612                         inputblock
15613                     
15614                 ];
15615                 
15616                 
15617         };
15618         
15619         if(this.boxLabel){
15620             cfg.cn.push({
15621                 tag: 'label',
15622                 'for': id,
15623                 cls: 'box-label',
15624                 html: this.boxLabel
15625             })
15626         }
15627         
15628         return cfg;
15629         
15630     },
15631     inputEl: function ()
15632     {
15633         return this.el.select('input.roo-radio',true).first();
15634     },
15635     onClick : function()
15636     {   
15637         this.setChecked(true);
15638     },
15639     
15640     setChecked : function(state,suppressEvent)
15641     {
15642         if(state){
15643             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
15644                 v.dom.checked = false;
15645             });
15646         }
15647         
15648         this.checked = state;
15649         this.inputEl().dom.checked = state;
15650         
15651         if(suppressEvent !== true){
15652             this.fireEvent('check', this, state);
15653         }
15654         
15655         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15656         
15657     },
15658     
15659     getGroupValue : function()
15660     {
15661         var value = ''
15662         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
15663             if(v.dom.checked == true){
15664                 value = v.dom.value;
15665             }
15666         });
15667         
15668         return value;
15669     },
15670     
15671     /**
15672      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
15673      * @return {Mixed} value The field value
15674      */
15675     getValue : function(){
15676         return this.getGroupValue();
15677     }
15678     
15679 });
15680
15681  
15682 //<script type="text/javascript">
15683
15684 /*
15685  * Based  Ext JS Library 1.1.1
15686  * Copyright(c) 2006-2007, Ext JS, LLC.
15687  * LGPL
15688  *
15689  */
15690  
15691 /**
15692  * @class Roo.HtmlEditorCore
15693  * @extends Roo.Component
15694  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
15695  *
15696  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
15697  */
15698
15699 Roo.HtmlEditorCore = function(config){
15700     
15701     
15702     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
15703     this.addEvents({
15704         /**
15705          * @event initialize
15706          * Fires when the editor is fully initialized (including the iframe)
15707          * @param {Roo.HtmlEditorCore} this
15708          */
15709         initialize: true,
15710         /**
15711          * @event activate
15712          * Fires when the editor is first receives the focus. Any insertion must wait
15713          * until after this event.
15714          * @param {Roo.HtmlEditorCore} this
15715          */
15716         activate: true,
15717          /**
15718          * @event beforesync
15719          * Fires before the textarea is updated with content from the editor iframe. Return false
15720          * to cancel the sync.
15721          * @param {Roo.HtmlEditorCore} this
15722          * @param {String} html
15723          */
15724         beforesync: true,
15725          /**
15726          * @event beforepush
15727          * Fires before the iframe editor is updated with content from the textarea. Return false
15728          * to cancel the push.
15729          * @param {Roo.HtmlEditorCore} this
15730          * @param {String} html
15731          */
15732         beforepush: true,
15733          /**
15734          * @event sync
15735          * Fires when the textarea is updated with content from the editor iframe.
15736          * @param {Roo.HtmlEditorCore} this
15737          * @param {String} html
15738          */
15739         sync: true,
15740          /**
15741          * @event push
15742          * Fires when the iframe editor is updated with content from the textarea.
15743          * @param {Roo.HtmlEditorCore} this
15744          * @param {String} html
15745          */
15746         push: true,
15747         
15748         /**
15749          * @event editorevent
15750          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15751          * @param {Roo.HtmlEditorCore} this
15752          */
15753         editorevent: true
15754     });
15755      
15756 };
15757
15758
15759 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
15760
15761
15762      /**
15763      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
15764      */
15765     
15766     owner : false,
15767     
15768      /**
15769      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15770      *                        Roo.resizable.
15771      */
15772     resizable : false,
15773      /**
15774      * @cfg {Number} height (in pixels)
15775      */   
15776     height: 300,
15777    /**
15778      * @cfg {Number} width (in pixels)
15779      */   
15780     width: 500,
15781     
15782     /**
15783      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15784      * 
15785      */
15786     stylesheets: false,
15787     
15788     // id of frame..
15789     frameId: false,
15790     
15791     // private properties
15792     validationEvent : false,
15793     deferHeight: true,
15794     initialized : false,
15795     activated : false,
15796     sourceEditMode : false,
15797     onFocus : Roo.emptyFn,
15798     iframePad:3,
15799     hideMode:'offsets',
15800     
15801     clearUp: true,
15802     
15803      
15804     
15805
15806     /**
15807      * Protected method that will not generally be called directly. It
15808      * is called when the editor initializes the iframe with HTML contents. Override this method if you
15809      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
15810      */
15811     getDocMarkup : function(){
15812         // body styles..
15813         var st = '';
15814         Roo.log(this.stylesheets);
15815         
15816         // inherit styels from page...?? 
15817         if (this.stylesheets === false) {
15818             
15819             Roo.get(document.head).select('style').each(function(node) {
15820                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
15821             });
15822             
15823             Roo.get(document.head).select('link').each(function(node) { 
15824                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
15825             });
15826             
15827         } else if (!this.stylesheets.length) {
15828                 // simple..
15829                 st = '<style type="text/css">' +
15830                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
15831                    '</style>';
15832         } else {
15833             Roo.each(this.stylesheets, function(s) {
15834                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
15835             });
15836             
15837         }
15838         
15839         st +=  '<style type="text/css">' +
15840             'IMG { cursor: pointer } ' +
15841         '</style>';
15842
15843         
15844         return '<html><head>' + st  +
15845             //<style type="text/css">' +
15846             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
15847             //'</style>' +
15848             ' </head><body class="roo-htmleditor-body"></body></html>';
15849     },
15850
15851     // private
15852     onRender : function(ct, position)
15853     {
15854         var _t = this;
15855         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
15856         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
15857         
15858         
15859         this.el.dom.style.border = '0 none';
15860         this.el.dom.setAttribute('tabIndex', -1);
15861         this.el.addClass('x-hidden hide');
15862         
15863         
15864         
15865         if(Roo.isIE){ // fix IE 1px bogus margin
15866             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
15867         }
15868        
15869         
15870         this.frameId = Roo.id();
15871         
15872          
15873         
15874         var iframe = this.owner.wrap.createChild({
15875             tag: 'iframe',
15876             cls: 'form-control', // bootstrap..
15877             id: this.frameId,
15878             name: this.frameId,
15879             frameBorder : 'no',
15880             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
15881         }, this.el
15882         );
15883         
15884         
15885         this.iframe = iframe.dom;
15886
15887          this.assignDocWin();
15888         
15889         this.doc.designMode = 'on';
15890        
15891         this.doc.open();
15892         this.doc.write(this.getDocMarkup());
15893         this.doc.close();
15894
15895         
15896         var task = { // must defer to wait for browser to be ready
15897             run : function(){
15898                 //console.log("run task?" + this.doc.readyState);
15899                 this.assignDocWin();
15900                 if(this.doc.body || this.doc.readyState == 'complete'){
15901                     try {
15902                         this.doc.designMode="on";
15903                     } catch (e) {
15904                         return;
15905                     }
15906                     Roo.TaskMgr.stop(task);
15907                     this.initEditor.defer(10, this);
15908                 }
15909             },
15910             interval : 10,
15911             duration: 10000,
15912             scope: this
15913         };
15914         Roo.TaskMgr.start(task);
15915
15916         
15917          
15918     },
15919
15920     // private
15921     onResize : function(w, h)
15922     {
15923          Roo.log('resize: ' +w + ',' + h );
15924         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
15925         if(!this.iframe){
15926             return;
15927         }
15928         if(typeof w == 'number'){
15929             
15930             this.iframe.style.width = w + 'px';
15931         }
15932         if(typeof h == 'number'){
15933             
15934             this.iframe.style.height = h + 'px';
15935             if(this.doc){
15936                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
15937             }
15938         }
15939         
15940     },
15941
15942     /**
15943      * Toggles the editor between standard and source edit mode.
15944      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15945      */
15946     toggleSourceEdit : function(sourceEditMode){
15947         
15948         this.sourceEditMode = sourceEditMode === true;
15949         
15950         if(this.sourceEditMode){
15951  
15952             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
15953             
15954         }else{
15955             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
15956             //this.iframe.className = '';
15957             this.deferFocus();
15958         }
15959         //this.setSize(this.owner.wrap.getSize());
15960         //this.fireEvent('editmodechange', this, this.sourceEditMode);
15961     },
15962
15963     
15964   
15965
15966     /**
15967      * Protected method that will not generally be called directly. If you need/want
15968      * custom HTML cleanup, this is the method you should override.
15969      * @param {String} html The HTML to be cleaned
15970      * return {String} The cleaned HTML
15971      */
15972     cleanHtml : function(html){
15973         html = String(html);
15974         if(html.length > 5){
15975             if(Roo.isSafari){ // strip safari nonsense
15976                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
15977             }
15978         }
15979         if(html == '&nbsp;'){
15980             html = '';
15981         }
15982         return html;
15983     },
15984
15985     /**
15986      * HTML Editor -> Textarea
15987      * Protected method that will not generally be called directly. Syncs the contents
15988      * of the editor iframe with the textarea.
15989      */
15990     syncValue : function(){
15991         if(this.initialized){
15992             var bd = (this.doc.body || this.doc.documentElement);
15993             //this.cleanUpPaste(); -- this is done else where and causes havoc..
15994             var html = bd.innerHTML;
15995             if(Roo.isSafari){
15996                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
15997                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
15998                 if(m && m[1]){
15999                     html = '<div style="'+m[0]+'">' + html + '</div>';
16000                 }
16001             }
16002             html = this.cleanHtml(html);
16003             // fix up the special chars.. normaly like back quotes in word...
16004             // however we do not want to do this with chinese..
16005             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16006                 var cc = b.charCodeAt();
16007                 if (
16008                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16009                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16010                     (cc >= 0xf900 && cc < 0xfb00 )
16011                 ) {
16012                         return b;
16013                 }
16014                 return "&#"+cc+";" 
16015             });
16016             if(this.owner.fireEvent('beforesync', this, html) !== false){
16017                 this.el.dom.value = html;
16018                 this.owner.fireEvent('sync', this, html);
16019             }
16020         }
16021     },
16022
16023     /**
16024      * Protected method that will not generally be called directly. Pushes the value of the textarea
16025      * into the iframe editor.
16026      */
16027     pushValue : function(){
16028         if(this.initialized){
16029             var v = this.el.dom.value.trim();
16030             
16031 //            if(v.length < 1){
16032 //                v = '&#160;';
16033 //            }
16034             
16035             if(this.owner.fireEvent('beforepush', this, v) !== false){
16036                 var d = (this.doc.body || this.doc.documentElement);
16037                 d.innerHTML = v;
16038                 this.cleanUpPaste();
16039                 this.el.dom.value = d.innerHTML;
16040                 this.owner.fireEvent('push', this, v);
16041             }
16042         }
16043     },
16044
16045     // private
16046     deferFocus : function(){
16047         this.focus.defer(10, this);
16048     },
16049
16050     // doc'ed in Field
16051     focus : function(){
16052         if(this.win && !this.sourceEditMode){
16053             this.win.focus();
16054         }else{
16055             this.el.focus();
16056         }
16057     },
16058     
16059     assignDocWin: function()
16060     {
16061         var iframe = this.iframe;
16062         
16063          if(Roo.isIE){
16064             this.doc = iframe.contentWindow.document;
16065             this.win = iframe.contentWindow;
16066         } else {
16067             if (!Roo.get(this.frameId)) {
16068                 return;
16069             }
16070             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16071             this.win = Roo.get(this.frameId).dom.contentWindow;
16072         }
16073     },
16074     
16075     // private
16076     initEditor : function(){
16077         //console.log("INIT EDITOR");
16078         this.assignDocWin();
16079         
16080         
16081         
16082         this.doc.designMode="on";
16083         this.doc.open();
16084         this.doc.write(this.getDocMarkup());
16085         this.doc.close();
16086         
16087         var dbody = (this.doc.body || this.doc.documentElement);
16088         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16089         // this copies styles from the containing element into thsi one..
16090         // not sure why we need all of this..
16091         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16092         
16093         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16094         //ss['background-attachment'] = 'fixed'; // w3c
16095         dbody.bgProperties = 'fixed'; // ie
16096         //Roo.DomHelper.applyStyles(dbody, ss);
16097         Roo.EventManager.on(this.doc, {
16098             //'mousedown': this.onEditorEvent,
16099             'mouseup': this.onEditorEvent,
16100             'dblclick': this.onEditorEvent,
16101             'click': this.onEditorEvent,
16102             'keyup': this.onEditorEvent,
16103             buffer:100,
16104             scope: this
16105         });
16106         if(Roo.isGecko){
16107             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16108         }
16109         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16110             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16111         }
16112         this.initialized = true;
16113
16114         this.owner.fireEvent('initialize', this);
16115         this.pushValue();
16116     },
16117
16118     // private
16119     onDestroy : function(){
16120         
16121         
16122         
16123         if(this.rendered){
16124             
16125             //for (var i =0; i < this.toolbars.length;i++) {
16126             //    // fixme - ask toolbars for heights?
16127             //    this.toolbars[i].onDestroy();
16128            // }
16129             
16130             //this.wrap.dom.innerHTML = '';
16131             //this.wrap.remove();
16132         }
16133     },
16134
16135     // private
16136     onFirstFocus : function(){
16137         
16138         this.assignDocWin();
16139         
16140         
16141         this.activated = true;
16142          
16143     
16144         if(Roo.isGecko){ // prevent silly gecko errors
16145             this.win.focus();
16146             var s = this.win.getSelection();
16147             if(!s.focusNode || s.focusNode.nodeType != 3){
16148                 var r = s.getRangeAt(0);
16149                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16150                 r.collapse(true);
16151                 this.deferFocus();
16152             }
16153             try{
16154                 this.execCmd('useCSS', true);
16155                 this.execCmd('styleWithCSS', false);
16156             }catch(e){}
16157         }
16158         this.owner.fireEvent('activate', this);
16159     },
16160
16161     // private
16162     adjustFont: function(btn){
16163         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16164         //if(Roo.isSafari){ // safari
16165         //    adjust *= 2;
16166        // }
16167         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16168         if(Roo.isSafari){ // safari
16169             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16170             v =  (v < 10) ? 10 : v;
16171             v =  (v > 48) ? 48 : v;
16172             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16173             
16174         }
16175         
16176         
16177         v = Math.max(1, v+adjust);
16178         
16179         this.execCmd('FontSize', v  );
16180     },
16181
16182     onEditorEvent : function(e){
16183         this.owner.fireEvent('editorevent', this, e);
16184       //  this.updateToolbar();
16185         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16186     },
16187
16188     insertTag : function(tg)
16189     {
16190         // could be a bit smarter... -> wrap the current selected tRoo..
16191         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16192             
16193             range = this.createRange(this.getSelection());
16194             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16195             wrappingNode.appendChild(range.extractContents());
16196             range.insertNode(wrappingNode);
16197
16198             return;
16199             
16200             
16201             
16202         }
16203         this.execCmd("formatblock",   tg);
16204         
16205     },
16206     
16207     insertText : function(txt)
16208     {
16209         
16210         
16211         var range = this.createRange();
16212         range.deleteContents();
16213                //alert(Sender.getAttribute('label'));
16214                
16215         range.insertNode(this.doc.createTextNode(txt));
16216     } ,
16217     
16218      
16219
16220     /**
16221      * Executes a Midas editor command on the editor document and performs necessary focus and
16222      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16223      * @param {String} cmd The Midas command
16224      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16225      */
16226     relayCmd : function(cmd, value){
16227         this.win.focus();
16228         this.execCmd(cmd, value);
16229         this.owner.fireEvent('editorevent', this);
16230         //this.updateToolbar();
16231         this.owner.deferFocus();
16232     },
16233
16234     /**
16235      * Executes a Midas editor command directly on the editor document.
16236      * For visual commands, you should use {@link #relayCmd} instead.
16237      * <b>This should only be called after the editor is initialized.</b>
16238      * @param {String} cmd The Midas command
16239      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16240      */
16241     execCmd : function(cmd, value){
16242         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16243         this.syncValue();
16244     },
16245  
16246  
16247    
16248     /**
16249      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16250      * to insert tRoo.
16251      * @param {String} text | dom node.. 
16252      */
16253     insertAtCursor : function(text)
16254     {
16255         
16256         
16257         
16258         if(!this.activated){
16259             return;
16260         }
16261         /*
16262         if(Roo.isIE){
16263             this.win.focus();
16264             var r = this.doc.selection.createRange();
16265             if(r){
16266                 r.collapse(true);
16267                 r.pasteHTML(text);
16268                 this.syncValue();
16269                 this.deferFocus();
16270             
16271             }
16272             return;
16273         }
16274         */
16275         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16276             this.win.focus();
16277             
16278             
16279             // from jquery ui (MIT licenced)
16280             var range, node;
16281             var win = this.win;
16282             
16283             if (win.getSelection && win.getSelection().getRangeAt) {
16284                 range = win.getSelection().getRangeAt(0);
16285                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16286                 range.insertNode(node);
16287             } else if (win.document.selection && win.document.selection.createRange) {
16288                 // no firefox support
16289                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16290                 win.document.selection.createRange().pasteHTML(txt);
16291             } else {
16292                 // no firefox support
16293                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16294                 this.execCmd('InsertHTML', txt);
16295             } 
16296             
16297             this.syncValue();
16298             
16299             this.deferFocus();
16300         }
16301     },
16302  // private
16303     mozKeyPress : function(e){
16304         if(e.ctrlKey){
16305             var c = e.getCharCode(), cmd;
16306           
16307             if(c > 0){
16308                 c = String.fromCharCode(c).toLowerCase();
16309                 switch(c){
16310                     case 'b':
16311                         cmd = 'bold';
16312                         break;
16313                     case 'i':
16314                         cmd = 'italic';
16315                         break;
16316                     
16317                     case 'u':
16318                         cmd = 'underline';
16319                         break;
16320                     
16321                     case 'v':
16322                         this.cleanUpPaste.defer(100, this);
16323                         return;
16324                         
16325                 }
16326                 if(cmd){
16327                     this.win.focus();
16328                     this.execCmd(cmd);
16329                     this.deferFocus();
16330                     e.preventDefault();
16331                 }
16332                 
16333             }
16334         }
16335     },
16336
16337     // private
16338     fixKeys : function(){ // load time branching for fastest keydown performance
16339         if(Roo.isIE){
16340             return function(e){
16341                 var k = e.getKey(), r;
16342                 if(k == e.TAB){
16343                     e.stopEvent();
16344                     r = this.doc.selection.createRange();
16345                     if(r){
16346                         r.collapse(true);
16347                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16348                         this.deferFocus();
16349                     }
16350                     return;
16351                 }
16352                 
16353                 if(k == e.ENTER){
16354                     r = this.doc.selection.createRange();
16355                     if(r){
16356                         var target = r.parentElement();
16357                         if(!target || target.tagName.toLowerCase() != 'li'){
16358                             e.stopEvent();
16359                             r.pasteHTML('<br />');
16360                             r.collapse(false);
16361                             r.select();
16362                         }
16363                     }
16364                 }
16365                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16366                     this.cleanUpPaste.defer(100, this);
16367                     return;
16368                 }
16369                 
16370                 
16371             };
16372         }else if(Roo.isOpera){
16373             return function(e){
16374                 var k = e.getKey();
16375                 if(k == e.TAB){
16376                     e.stopEvent();
16377                     this.win.focus();
16378                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16379                     this.deferFocus();
16380                 }
16381                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16382                     this.cleanUpPaste.defer(100, this);
16383                     return;
16384                 }
16385                 
16386             };
16387         }else if(Roo.isSafari){
16388             return function(e){
16389                 var k = e.getKey();
16390                 
16391                 if(k == e.TAB){
16392                     e.stopEvent();
16393                     this.execCmd('InsertText','\t');
16394                     this.deferFocus();
16395                     return;
16396                 }
16397                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16398                     this.cleanUpPaste.defer(100, this);
16399                     return;
16400                 }
16401                 
16402              };
16403         }
16404     }(),
16405     
16406     getAllAncestors: function()
16407     {
16408         var p = this.getSelectedNode();
16409         var a = [];
16410         if (!p) {
16411             a.push(p); // push blank onto stack..
16412             p = this.getParentElement();
16413         }
16414         
16415         
16416         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16417             a.push(p);
16418             p = p.parentNode;
16419         }
16420         a.push(this.doc.body);
16421         return a;
16422     },
16423     lastSel : false,
16424     lastSelNode : false,
16425     
16426     
16427     getSelection : function() 
16428     {
16429         this.assignDocWin();
16430         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16431     },
16432     
16433     getSelectedNode: function() 
16434     {
16435         // this may only work on Gecko!!!
16436         
16437         // should we cache this!!!!
16438         
16439         
16440         
16441          
16442         var range = this.createRange(this.getSelection()).cloneRange();
16443         
16444         if (Roo.isIE) {
16445             var parent = range.parentElement();
16446             while (true) {
16447                 var testRange = range.duplicate();
16448                 testRange.moveToElementText(parent);
16449                 if (testRange.inRange(range)) {
16450                     break;
16451                 }
16452                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
16453                     break;
16454                 }
16455                 parent = parent.parentElement;
16456             }
16457             return parent;
16458         }
16459         
16460         // is ancestor a text element.
16461         var ac =  range.commonAncestorContainer;
16462         if (ac.nodeType == 3) {
16463             ac = ac.parentNode;
16464         }
16465         
16466         var ar = ac.childNodes;
16467          
16468         var nodes = [];
16469         var other_nodes = [];
16470         var has_other_nodes = false;
16471         for (var i=0;i<ar.length;i++) {
16472             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
16473                 continue;
16474             }
16475             // fullly contained node.
16476             
16477             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
16478                 nodes.push(ar[i]);
16479                 continue;
16480             }
16481             
16482             // probably selected..
16483             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
16484                 other_nodes.push(ar[i]);
16485                 continue;
16486             }
16487             // outer..
16488             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
16489                 continue;
16490             }
16491             
16492             
16493             has_other_nodes = true;
16494         }
16495         if (!nodes.length && other_nodes.length) {
16496             nodes= other_nodes;
16497         }
16498         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
16499             return false;
16500         }
16501         
16502         return nodes[0];
16503     },
16504     createRange: function(sel)
16505     {
16506         // this has strange effects when using with 
16507         // top toolbar - not sure if it's a great idea.
16508         //this.editor.contentWindow.focus();
16509         if (typeof sel != "undefined") {
16510             try {
16511                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
16512             } catch(e) {
16513                 return this.doc.createRange();
16514             }
16515         } else {
16516             return this.doc.createRange();
16517         }
16518     },
16519     getParentElement: function()
16520     {
16521         
16522         this.assignDocWin();
16523         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
16524         
16525         var range = this.createRange(sel);
16526          
16527         try {
16528             var p = range.commonAncestorContainer;
16529             while (p.nodeType == 3) { // text node
16530                 p = p.parentNode;
16531             }
16532             return p;
16533         } catch (e) {
16534             return null;
16535         }
16536     
16537     },
16538     /***
16539      *
16540      * Range intersection.. the hard stuff...
16541      *  '-1' = before
16542      *  '0' = hits..
16543      *  '1' = after.
16544      *         [ -- selected range --- ]
16545      *   [fail]                        [fail]
16546      *
16547      *    basically..
16548      *      if end is before start or  hits it. fail.
16549      *      if start is after end or hits it fail.
16550      *
16551      *   if either hits (but other is outside. - then it's not 
16552      *   
16553      *    
16554      **/
16555     
16556     
16557     // @see http://www.thismuchiknow.co.uk/?p=64.
16558     rangeIntersectsNode : function(range, node)
16559     {
16560         var nodeRange = node.ownerDocument.createRange();
16561         try {
16562             nodeRange.selectNode(node);
16563         } catch (e) {
16564             nodeRange.selectNodeContents(node);
16565         }
16566     
16567         var rangeStartRange = range.cloneRange();
16568         rangeStartRange.collapse(true);
16569     
16570         var rangeEndRange = range.cloneRange();
16571         rangeEndRange.collapse(false);
16572     
16573         var nodeStartRange = nodeRange.cloneRange();
16574         nodeStartRange.collapse(true);
16575     
16576         var nodeEndRange = nodeRange.cloneRange();
16577         nodeEndRange.collapse(false);
16578     
16579         return rangeStartRange.compareBoundaryPoints(
16580                  Range.START_TO_START, nodeEndRange) == -1 &&
16581                rangeEndRange.compareBoundaryPoints(
16582                  Range.START_TO_START, nodeStartRange) == 1;
16583         
16584          
16585     },
16586     rangeCompareNode : function(range, node)
16587     {
16588         var nodeRange = node.ownerDocument.createRange();
16589         try {
16590             nodeRange.selectNode(node);
16591         } catch (e) {
16592             nodeRange.selectNodeContents(node);
16593         }
16594         
16595         
16596         range.collapse(true);
16597     
16598         nodeRange.collapse(true);
16599      
16600         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
16601         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
16602          
16603         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
16604         
16605         var nodeIsBefore   =  ss == 1;
16606         var nodeIsAfter    = ee == -1;
16607         
16608         if (nodeIsBefore && nodeIsAfter)
16609             return 0; // outer
16610         if (!nodeIsBefore && nodeIsAfter)
16611             return 1; //right trailed.
16612         
16613         if (nodeIsBefore && !nodeIsAfter)
16614             return 2;  // left trailed.
16615         // fully contined.
16616         return 3;
16617     },
16618
16619     // private? - in a new class?
16620     cleanUpPaste :  function()
16621     {
16622         // cleans up the whole document..
16623         Roo.log('cleanuppaste');
16624         
16625         this.cleanUpChildren(this.doc.body);
16626         var clean = this.cleanWordChars(this.doc.body.innerHTML);
16627         if (clean != this.doc.body.innerHTML) {
16628             this.doc.body.innerHTML = clean;
16629         }
16630         
16631     },
16632     
16633     cleanWordChars : function(input) {// change the chars to hex code
16634         var he = Roo.HtmlEditorCore;
16635         
16636         var output = input;
16637         Roo.each(he.swapCodes, function(sw) { 
16638             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
16639             
16640             output = output.replace(swapper, sw[1]);
16641         });
16642         
16643         return output;
16644     },
16645     
16646     
16647     cleanUpChildren : function (n)
16648     {
16649         if (!n.childNodes.length) {
16650             return;
16651         }
16652         for (var i = n.childNodes.length-1; i > -1 ; i--) {
16653            this.cleanUpChild(n.childNodes[i]);
16654         }
16655     },
16656     
16657     
16658         
16659     
16660     cleanUpChild : function (node)
16661     {
16662         var ed = this;
16663         //console.log(node);
16664         if (node.nodeName == "#text") {
16665             // clean up silly Windows -- stuff?
16666             return; 
16667         }
16668         if (node.nodeName == "#comment") {
16669             node.parentNode.removeChild(node);
16670             // clean up silly Windows -- stuff?
16671             return; 
16672         }
16673         
16674         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
16675             // remove node.
16676             node.parentNode.removeChild(node);
16677             return;
16678             
16679         }
16680         
16681         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
16682         
16683         // remove <a name=....> as rendering on yahoo mailer is borked with this.
16684         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
16685         
16686         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
16687         //    remove_keep_children = true;
16688         //}
16689         
16690         if (remove_keep_children) {
16691             this.cleanUpChildren(node);
16692             // inserts everything just before this node...
16693             while (node.childNodes.length) {
16694                 var cn = node.childNodes[0];
16695                 node.removeChild(cn);
16696                 node.parentNode.insertBefore(cn, node);
16697             }
16698             node.parentNode.removeChild(node);
16699             return;
16700         }
16701         
16702         if (!node.attributes || !node.attributes.length) {
16703             this.cleanUpChildren(node);
16704             return;
16705         }
16706         
16707         function cleanAttr(n,v)
16708         {
16709             
16710             if (v.match(/^\./) || v.match(/^\//)) {
16711                 return;
16712             }
16713             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
16714                 return;
16715             }
16716             if (v.match(/^#/)) {
16717                 return;
16718             }
16719 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
16720             node.removeAttribute(n);
16721             
16722         }
16723         
16724         function cleanStyle(n,v)
16725         {
16726             if (v.match(/expression/)) { //XSS?? should we even bother..
16727                 node.removeAttribute(n);
16728                 return;
16729             }
16730             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
16731             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
16732             
16733             
16734             var parts = v.split(/;/);
16735             var clean = [];
16736             
16737             Roo.each(parts, function(p) {
16738                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
16739                 if (!p.length) {
16740                     return true;
16741                 }
16742                 var l = p.split(':').shift().replace(/\s+/g,'');
16743                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
16744                 
16745                 if ( cblack.indexOf(l) > -1) {
16746 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
16747                     //node.removeAttribute(n);
16748                     return true;
16749                 }
16750                 //Roo.log()
16751                 // only allow 'c whitelisted system attributes'
16752                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
16753 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
16754                     //node.removeAttribute(n);
16755                     return true;
16756                 }
16757                 
16758                 
16759                  
16760                 
16761                 clean.push(p);
16762                 return true;
16763             });
16764             if (clean.length) { 
16765                 node.setAttribute(n, clean.join(';'));
16766             } else {
16767                 node.removeAttribute(n);
16768             }
16769             
16770         }
16771         
16772         
16773         for (var i = node.attributes.length-1; i > -1 ; i--) {
16774             var a = node.attributes[i];
16775             //console.log(a);
16776             
16777             if (a.name.toLowerCase().substr(0,2)=='on')  {
16778                 node.removeAttribute(a.name);
16779                 continue;
16780             }
16781             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
16782                 node.removeAttribute(a.name);
16783                 continue;
16784             }
16785             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
16786                 cleanAttr(a.name,a.value); // fixme..
16787                 continue;
16788             }
16789             if (a.name == 'style') {
16790                 cleanStyle(a.name,a.value);
16791                 continue;
16792             }
16793             /// clean up MS crap..
16794             // tecnically this should be a list of valid class'es..
16795             
16796             
16797             if (a.name == 'class') {
16798                 if (a.value.match(/^Mso/)) {
16799                     node.className = '';
16800                 }
16801                 
16802                 if (a.value.match(/body/)) {
16803                     node.className = '';
16804                 }
16805                 continue;
16806             }
16807             
16808             // style cleanup!?
16809             // class cleanup?
16810             
16811         }
16812         
16813         
16814         this.cleanUpChildren(node);
16815         
16816         
16817     },
16818     /**
16819      * Clean up MS wordisms...
16820      */
16821     cleanWord : function(node)
16822     {
16823         var _t = this;
16824         var cleanWordChildren = function()
16825         {
16826             if (!node.childNodes.length) {
16827                 return;
16828             }
16829             for (var i = node.childNodes.length-1; i > -1 ; i--) {
16830                _t.cleanWord(node.childNodes[i]);
16831             }
16832         }
16833         
16834         
16835         if (!node) {
16836             this.cleanWord(this.doc.body);
16837             return;
16838         }
16839         if (node.nodeName == "#text") {
16840             // clean up silly Windows -- stuff?
16841             return; 
16842         }
16843         if (node.nodeName == "#comment") {
16844             node.parentNode.removeChild(node);
16845             // clean up silly Windows -- stuff?
16846             return; 
16847         }
16848         
16849         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
16850             node.parentNode.removeChild(node);
16851             return;
16852         }
16853         
16854         // remove - but keep children..
16855         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
16856             while (node.childNodes.length) {
16857                 var cn = node.childNodes[0];
16858                 node.removeChild(cn);
16859                 node.parentNode.insertBefore(cn, node);
16860             }
16861             node.parentNode.removeChild(node);
16862             cleanWordChildren();
16863             return;
16864         }
16865         // clean styles
16866         if (node.className.length) {
16867             
16868             var cn = node.className.split(/\W+/);
16869             var cna = [];
16870             Roo.each(cn, function(cls) {
16871                 if (cls.match(/Mso[a-zA-Z]+/)) {
16872                     return;
16873                 }
16874                 cna.push(cls);
16875             });
16876             node.className = cna.length ? cna.join(' ') : '';
16877             if (!cna.length) {
16878                 node.removeAttribute("class");
16879             }
16880         }
16881         
16882         if (node.hasAttribute("lang")) {
16883             node.removeAttribute("lang");
16884         }
16885         
16886         if (node.hasAttribute("style")) {
16887             
16888             var styles = node.getAttribute("style").split(";");
16889             var nstyle = [];
16890             Roo.each(styles, function(s) {
16891                 if (!s.match(/:/)) {
16892                     return;
16893                 }
16894                 var kv = s.split(":");
16895                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
16896                     return;
16897                 }
16898                 // what ever is left... we allow.
16899                 nstyle.push(s);
16900             });
16901             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
16902             if (!nstyle.length) {
16903                 node.removeAttribute('style');
16904             }
16905         }
16906         
16907         cleanWordChildren();
16908         
16909         
16910     },
16911     domToHTML : function(currentElement, depth, nopadtext) {
16912         
16913             depth = depth || 0;
16914             nopadtext = nopadtext || false;
16915         
16916             if (!currentElement) {
16917                 return this.domToHTML(this.doc.body);
16918             }
16919             
16920             //Roo.log(currentElement);
16921             var j;
16922             var allText = false;
16923             var nodeName = currentElement.nodeName;
16924             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
16925             
16926             if  (nodeName == '#text') {
16927                 return currentElement.nodeValue;
16928             }
16929             
16930             
16931             var ret = '';
16932             if (nodeName != 'BODY') {
16933                  
16934                 var i = 0;
16935                 // Prints the node tagName, such as <A>, <IMG>, etc
16936                 if (tagName) {
16937                     var attr = [];
16938                     for(i = 0; i < currentElement.attributes.length;i++) {
16939                         // quoting?
16940                         var aname = currentElement.attributes.item(i).name;
16941                         if (!currentElement.attributes.item(i).value.length) {
16942                             continue;
16943                         }
16944                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
16945                     }
16946                     
16947                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
16948                 } 
16949                 else {
16950                     
16951                     // eack
16952                 }
16953             } else {
16954                 tagName = false;
16955             }
16956             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
16957                 return ret;
16958             }
16959             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
16960                 nopadtext = true;
16961             }
16962             
16963             
16964             // Traverse the tree
16965             i = 0;
16966             var currentElementChild = currentElement.childNodes.item(i);
16967             var allText = true;
16968             var innerHTML  = '';
16969             lastnode = '';
16970             while (currentElementChild) {
16971                 // Formatting code (indent the tree so it looks nice on the screen)
16972                 var nopad = nopadtext;
16973                 if (lastnode == 'SPAN') {
16974                     nopad  = true;
16975                 }
16976                 // text
16977                 if  (currentElementChild.nodeName == '#text') {
16978                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
16979                     if (!nopad && toadd.length > 80) {
16980                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
16981                     }
16982                     innerHTML  += toadd;
16983                     
16984                     i++;
16985                     currentElementChild = currentElement.childNodes.item(i);
16986                     lastNode = '';
16987                     continue;
16988                 }
16989                 allText = false;
16990                 
16991                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
16992                     
16993                 // Recursively traverse the tree structure of the child node
16994                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
16995                 lastnode = currentElementChild.nodeName;
16996                 i++;
16997                 currentElementChild=currentElement.childNodes.item(i);
16998             }
16999             
17000             ret += innerHTML;
17001             
17002             if (!allText) {
17003                     // The remaining code is mostly for formatting the tree
17004                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17005             }
17006             
17007             
17008             if (tagName) {
17009                 ret+= "</"+tagName+">";
17010             }
17011             return ret;
17012             
17013         }
17014     
17015     // hide stuff that is not compatible
17016     /**
17017      * @event blur
17018      * @hide
17019      */
17020     /**
17021      * @event change
17022      * @hide
17023      */
17024     /**
17025      * @event focus
17026      * @hide
17027      */
17028     /**
17029      * @event specialkey
17030      * @hide
17031      */
17032     /**
17033      * @cfg {String} fieldClass @hide
17034      */
17035     /**
17036      * @cfg {String} focusClass @hide
17037      */
17038     /**
17039      * @cfg {String} autoCreate @hide
17040      */
17041     /**
17042      * @cfg {String} inputType @hide
17043      */
17044     /**
17045      * @cfg {String} invalidClass @hide
17046      */
17047     /**
17048      * @cfg {String} invalidText @hide
17049      */
17050     /**
17051      * @cfg {String} msgFx @hide
17052      */
17053     /**
17054      * @cfg {String} validateOnBlur @hide
17055      */
17056 });
17057
17058 Roo.HtmlEditorCore.white = [
17059         'area', 'br', 'img', 'input', 'hr', 'wbr',
17060         
17061        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17062        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17063        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17064        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17065        'table',   'ul',         'xmp', 
17066        
17067        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17068       'thead',   'tr', 
17069      
17070       'dir', 'menu', 'ol', 'ul', 'dl',
17071        
17072       'embed',  'object'
17073 ];
17074
17075
17076 Roo.HtmlEditorCore.black = [
17077     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17078         'applet', // 
17079         'base',   'basefont', 'bgsound', 'blink',  'body', 
17080         'frame',  'frameset', 'head',    'html',   'ilayer', 
17081         'iframe', 'layer',  'link',     'meta',    'object',   
17082         'script', 'style' ,'title',  'xml' // clean later..
17083 ];
17084 Roo.HtmlEditorCore.clean = [
17085     'script', 'style', 'title', 'xml'
17086 ];
17087 Roo.HtmlEditorCore.remove = [
17088     'font'
17089 ];
17090 // attributes..
17091
17092 Roo.HtmlEditorCore.ablack = [
17093     'on'
17094 ];
17095     
17096 Roo.HtmlEditorCore.aclean = [ 
17097     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17098 ];
17099
17100 // protocols..
17101 Roo.HtmlEditorCore.pwhite= [
17102         'http',  'https',  'mailto'
17103 ];
17104
17105 // white listed style attributes.
17106 Roo.HtmlEditorCore.cwhite= [
17107       //  'text-align', /// default is to allow most things..
17108       
17109          
17110 //        'font-size'//??
17111 ];
17112
17113 // black listed style attributes.
17114 Roo.HtmlEditorCore.cblack= [
17115       //  'font-size' -- this can be set by the project 
17116 ];
17117
17118
17119 Roo.HtmlEditorCore.swapCodes   =[ 
17120     [    8211, "--" ], 
17121     [    8212, "--" ], 
17122     [    8216,  "'" ],  
17123     [    8217, "'" ],  
17124     [    8220, '"' ],  
17125     [    8221, '"' ],  
17126     [    8226, "*" ],  
17127     [    8230, "..." ]
17128 ]; 
17129
17130     /*
17131  * - LGPL
17132  *
17133  * HtmlEditor
17134  * 
17135  */
17136
17137 /**
17138  * @class Roo.bootstrap.HtmlEditor
17139  * @extends Roo.bootstrap.TextArea
17140  * Bootstrap HtmlEditor class
17141
17142  * @constructor
17143  * Create a new HtmlEditor
17144  * @param {Object} config The config object
17145  */
17146
17147 Roo.bootstrap.HtmlEditor = function(config){
17148     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17149     if (!this.toolbars) {
17150         this.toolbars = [];
17151     }
17152     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17153     this.addEvents({
17154             /**
17155              * @event initialize
17156              * Fires when the editor is fully initialized (including the iframe)
17157              * @param {HtmlEditor} this
17158              */
17159             initialize: true,
17160             /**
17161              * @event activate
17162              * Fires when the editor is first receives the focus. Any insertion must wait
17163              * until after this event.
17164              * @param {HtmlEditor} this
17165              */
17166             activate: true,
17167              /**
17168              * @event beforesync
17169              * Fires before the textarea is updated with content from the editor iframe. Return false
17170              * to cancel the sync.
17171              * @param {HtmlEditor} this
17172              * @param {String} html
17173              */
17174             beforesync: true,
17175              /**
17176              * @event beforepush
17177              * Fires before the iframe editor is updated with content from the textarea. Return false
17178              * to cancel the push.
17179              * @param {HtmlEditor} this
17180              * @param {String} html
17181              */
17182             beforepush: true,
17183              /**
17184              * @event sync
17185              * Fires when the textarea is updated with content from the editor iframe.
17186              * @param {HtmlEditor} this
17187              * @param {String} html
17188              */
17189             sync: true,
17190              /**
17191              * @event push
17192              * Fires when the iframe editor is updated with content from the textarea.
17193              * @param {HtmlEditor} this
17194              * @param {String} html
17195              */
17196             push: true,
17197              /**
17198              * @event editmodechange
17199              * Fires when the editor switches edit modes
17200              * @param {HtmlEditor} this
17201              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17202              */
17203             editmodechange: true,
17204             /**
17205              * @event editorevent
17206              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17207              * @param {HtmlEditor} this
17208              */
17209             editorevent: true,
17210             /**
17211              * @event firstfocus
17212              * Fires when on first focus - needed by toolbars..
17213              * @param {HtmlEditor} this
17214              */
17215             firstfocus: true,
17216             /**
17217              * @event autosave
17218              * Auto save the htmlEditor value as a file into Events
17219              * @param {HtmlEditor} this
17220              */
17221             autosave: true,
17222             /**
17223              * @event savedpreview
17224              * preview the saved version of htmlEditor
17225              * @param {HtmlEditor} this
17226              */
17227             savedpreview: true
17228         });
17229 };
17230
17231
17232 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17233     
17234     
17235       /**
17236      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17237      */
17238     toolbars : false,
17239    
17240      /**
17241      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17242      *                        Roo.resizable.
17243      */
17244     resizable : false,
17245      /**
17246      * @cfg {Number} height (in pixels)
17247      */   
17248     height: 300,
17249    /**
17250      * @cfg {Number} width (in pixels)
17251      */   
17252     width: false,
17253     
17254     /**
17255      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17256      * 
17257      */
17258     stylesheets: false,
17259     
17260     // id of frame..
17261     frameId: false,
17262     
17263     // private properties
17264     validationEvent : false,
17265     deferHeight: true,
17266     initialized : false,
17267     activated : false,
17268     
17269     onFocus : Roo.emptyFn,
17270     iframePad:3,
17271     hideMode:'offsets',
17272     
17273     
17274     tbContainer : false,
17275     
17276     toolbarContainer :function() {
17277         return this.wrap.select('.x-html-editor-tb',true).first();
17278     },
17279
17280     /**
17281      * Protected method that will not generally be called directly. It
17282      * is called when the editor creates its toolbar. Override this method if you need to
17283      * add custom toolbar buttons.
17284      * @param {HtmlEditor} editor
17285      */
17286     createToolbar : function(){
17287         
17288         Roo.log("create toolbars");
17289         
17290         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17291         this.toolbars[0].render(this.toolbarContainer());
17292         
17293         return;
17294         
17295 //        if (!editor.toolbars || !editor.toolbars.length) {
17296 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17297 //        }
17298 //        
17299 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17300 //            editor.toolbars[i] = Roo.factory(
17301 //                    typeof(editor.toolbars[i]) == 'string' ?
17302 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17303 //                Roo.bootstrap.HtmlEditor);
17304 //            editor.toolbars[i].init(editor);
17305 //        }
17306     },
17307
17308      
17309     // private
17310     onRender : function(ct, position)
17311     {
17312        // Roo.log("Call onRender: " + this.xtype);
17313         var _t = this;
17314         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17315       
17316         this.wrap = this.inputEl().wrap({
17317             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17318         });
17319         
17320         this.editorcore.onRender(ct, position);
17321          
17322         if (this.resizable) {
17323             this.resizeEl = new Roo.Resizable(this.wrap, {
17324                 pinned : true,
17325                 wrap: true,
17326                 dynamic : true,
17327                 minHeight : this.height,
17328                 height: this.height,
17329                 handles : this.resizable,
17330                 width: this.width,
17331                 listeners : {
17332                     resize : function(r, w, h) {
17333                         _t.onResize(w,h); // -something
17334                     }
17335                 }
17336             });
17337             
17338         }
17339         this.createToolbar(this);
17340        
17341         
17342         if(!this.width && this.resizable){
17343             this.setSize(this.wrap.getSize());
17344         }
17345         if (this.resizeEl) {
17346             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17347             // should trigger onReize..
17348         }
17349         
17350     },
17351
17352     // private
17353     onResize : function(w, h)
17354     {
17355         Roo.log('resize: ' +w + ',' + h );
17356         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17357         var ew = false;
17358         var eh = false;
17359         
17360         if(this.inputEl() ){
17361             if(typeof w == 'number'){
17362                 var aw = w - this.wrap.getFrameWidth('lr');
17363                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17364                 ew = aw;
17365             }
17366             if(typeof h == 'number'){
17367                  var tbh = -11;  // fixme it needs to tool bar size!
17368                 for (var i =0; i < this.toolbars.length;i++) {
17369                     // fixme - ask toolbars for heights?
17370                     tbh += this.toolbars[i].el.getHeight();
17371                     //if (this.toolbars[i].footer) {
17372                     //    tbh += this.toolbars[i].footer.el.getHeight();
17373                     //}
17374                 }
17375               
17376                 
17377                 
17378                 
17379                 
17380                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17381                 ah -= 5; // knock a few pixes off for look..
17382                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17383                 var eh = ah;
17384             }
17385         }
17386         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17387         this.editorcore.onResize(ew,eh);
17388         
17389     },
17390
17391     /**
17392      * Toggles the editor between standard and source edit mode.
17393      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17394      */
17395     toggleSourceEdit : function(sourceEditMode)
17396     {
17397         this.editorcore.toggleSourceEdit(sourceEditMode);
17398         
17399         if(this.editorcore.sourceEditMode){
17400             Roo.log('editor - showing textarea');
17401             
17402 //            Roo.log('in');
17403 //            Roo.log(this.syncValue());
17404             this.syncValue();
17405             this.inputEl().removeClass(['hide', 'x-hidden']);
17406             this.inputEl().dom.removeAttribute('tabIndex');
17407             this.inputEl().focus();
17408         }else{
17409             Roo.log('editor - hiding textarea');
17410 //            Roo.log('out')
17411 //            Roo.log(this.pushValue()); 
17412             this.pushValue();
17413             
17414             this.inputEl().addClass(['hide', 'x-hidden']);
17415             this.inputEl().dom.setAttribute('tabIndex', -1);
17416             //this.deferFocus();
17417         }
17418          
17419         if(this.resizable){
17420             this.setSize(this.wrap.getSize());
17421         }
17422         
17423         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17424     },
17425  
17426     // private (for BoxComponent)
17427     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17428
17429     // private (for BoxComponent)
17430     getResizeEl : function(){
17431         return this.wrap;
17432     },
17433
17434     // private (for BoxComponent)
17435     getPositionEl : function(){
17436         return this.wrap;
17437     },
17438
17439     // private
17440     initEvents : function(){
17441         this.originalValue = this.getValue();
17442     },
17443
17444 //    /**
17445 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17446 //     * @method
17447 //     */
17448 //    markInvalid : Roo.emptyFn,
17449 //    /**
17450 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17451 //     * @method
17452 //     */
17453 //    clearInvalid : Roo.emptyFn,
17454
17455     setValue : function(v){
17456         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
17457         this.editorcore.pushValue();
17458     },
17459
17460      
17461     // private
17462     deferFocus : function(){
17463         this.focus.defer(10, this);
17464     },
17465
17466     // doc'ed in Field
17467     focus : function(){
17468         this.editorcore.focus();
17469         
17470     },
17471       
17472
17473     // private
17474     onDestroy : function(){
17475         
17476         
17477         
17478         if(this.rendered){
17479             
17480             for (var i =0; i < this.toolbars.length;i++) {
17481                 // fixme - ask toolbars for heights?
17482                 this.toolbars[i].onDestroy();
17483             }
17484             
17485             this.wrap.dom.innerHTML = '';
17486             this.wrap.remove();
17487         }
17488     },
17489
17490     // private
17491     onFirstFocus : function(){
17492         //Roo.log("onFirstFocus");
17493         this.editorcore.onFirstFocus();
17494          for (var i =0; i < this.toolbars.length;i++) {
17495             this.toolbars[i].onFirstFocus();
17496         }
17497         
17498     },
17499     
17500     // private
17501     syncValue : function()
17502     {   
17503         this.editorcore.syncValue();
17504     },
17505     
17506     pushValue : function()
17507     {   
17508         this.editorcore.pushValue();
17509     }
17510      
17511     
17512     // hide stuff that is not compatible
17513     /**
17514      * @event blur
17515      * @hide
17516      */
17517     /**
17518      * @event change
17519      * @hide
17520      */
17521     /**
17522      * @event focus
17523      * @hide
17524      */
17525     /**
17526      * @event specialkey
17527      * @hide
17528      */
17529     /**
17530      * @cfg {String} fieldClass @hide
17531      */
17532     /**
17533      * @cfg {String} focusClass @hide
17534      */
17535     /**
17536      * @cfg {String} autoCreate @hide
17537      */
17538     /**
17539      * @cfg {String} inputType @hide
17540      */
17541     /**
17542      * @cfg {String} invalidClass @hide
17543      */
17544     /**
17545      * @cfg {String} invalidText @hide
17546      */
17547     /**
17548      * @cfg {String} msgFx @hide
17549      */
17550     /**
17551      * @cfg {String} validateOnBlur @hide
17552      */
17553 });
17554  
17555     
17556    
17557    
17558    
17559       
17560 Roo.namespace('Roo.bootstrap.htmleditor');
17561 /**
17562  * @class Roo.bootstrap.HtmlEditorToolbar1
17563  * Basic Toolbar
17564  * 
17565  * Usage:
17566  *
17567  new Roo.bootstrap.HtmlEditor({
17568     ....
17569     toolbars : [
17570         new Roo.bootstrap.HtmlEditorToolbar1({
17571             disable : { fonts: 1 , format: 1, ..., ... , ...],
17572             btns : [ .... ]
17573         })
17574     }
17575      
17576  * 
17577  * @cfg {Object} disable List of elements to disable..
17578  * @cfg {Array} btns List of additional buttons.
17579  * 
17580  * 
17581  * NEEDS Extra CSS? 
17582  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
17583  */
17584  
17585 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
17586 {
17587     
17588     Roo.apply(this, config);
17589     
17590     // default disabled, based on 'good practice'..
17591     this.disable = this.disable || {};
17592     Roo.applyIf(this.disable, {
17593         fontSize : true,
17594         colors : true,
17595         specialElements : true
17596     });
17597     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
17598     
17599     this.editor = config.editor;
17600     this.editorcore = config.editor.editorcore;
17601     
17602     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
17603     
17604     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
17605     // dont call parent... till later.
17606 }
17607 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
17608      
17609     bar : true,
17610     
17611     editor : false,
17612     editorcore : false,
17613     
17614     
17615     formats : [
17616         "p" ,  
17617         "h1","h2","h3","h4","h5","h6", 
17618         "pre", "code", 
17619         "abbr", "acronym", "address", "cite", "samp", "var",
17620         'div','span'
17621     ],
17622     
17623     onRender : function(ct, position)
17624     {
17625        // Roo.log("Call onRender: " + this.xtype);
17626         
17627        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
17628        Roo.log(this.el);
17629        this.el.dom.style.marginBottom = '0';
17630        var _this = this;
17631        var editorcore = this.editorcore;
17632        var editor= this.editor;
17633        
17634        var children = [];
17635        var btn = function(id,cmd , toggle, handler){
17636        
17637             var  event = toggle ? 'toggle' : 'click';
17638        
17639             var a = {
17640                 size : 'sm',
17641                 xtype: 'Button',
17642                 xns: Roo.bootstrap,
17643                 glyphicon : id,
17644                 cmd : id || cmd,
17645                 enableToggle:toggle !== false,
17646                 //html : 'submit'
17647                 pressed : toggle ? false : null,
17648                 listeners : {}
17649             }
17650             a.listeners[toggle ? 'toggle' : 'click'] = function() {
17651                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
17652             }
17653             children.push(a);
17654             return a;
17655        }
17656         
17657         var style = {
17658                 xtype: 'Button',
17659                 size : 'sm',
17660                 xns: Roo.bootstrap,
17661                 glyphicon : 'font',
17662                 //html : 'submit'
17663                 menu : {
17664                     xtype: 'Menu',
17665                     xns: Roo.bootstrap,
17666                     items:  []
17667                 }
17668         };
17669         Roo.each(this.formats, function(f) {
17670             style.menu.items.push({
17671                 xtype :'MenuItem',
17672                 xns: Roo.bootstrap,
17673                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
17674                 tagname : f,
17675                 listeners : {
17676                     click : function()
17677                     {
17678                         editorcore.insertTag(this.tagname);
17679                         editor.focus();
17680                     }
17681                 }
17682                 
17683             });
17684         });
17685          children.push(style);   
17686             
17687             
17688         btn('bold',false,true);
17689         btn('italic',false,true);
17690         btn('align-left', 'justifyleft',true);
17691         btn('align-center', 'justifycenter',true);
17692         btn('align-right' , 'justifyright',true);
17693         btn('link', false, false, function(btn) {
17694             //Roo.log("create link?");
17695             var url = prompt(this.createLinkText, this.defaultLinkValue);
17696             if(url && url != 'http:/'+'/'){
17697                 this.editorcore.relayCmd('createlink', url);
17698             }
17699         }),
17700         btn('list','insertunorderedlist',true);
17701         btn('pencil', false,true, function(btn){
17702                 Roo.log(this);
17703                 
17704                 this.toggleSourceEdit(btn.pressed);
17705         });
17706         /*
17707         var cog = {
17708                 xtype: 'Button',
17709                 size : 'sm',
17710                 xns: Roo.bootstrap,
17711                 glyphicon : 'cog',
17712                 //html : 'submit'
17713                 menu : {
17714                     xtype: 'Menu',
17715                     xns: Roo.bootstrap,
17716                     items:  []
17717                 }
17718         };
17719         
17720         cog.menu.items.push({
17721             xtype :'MenuItem',
17722             xns: Roo.bootstrap,
17723             html : Clean styles,
17724             tagname : f,
17725             listeners : {
17726                 click : function()
17727                 {
17728                     editorcore.insertTag(this.tagname);
17729                     editor.focus();
17730                 }
17731             }
17732             
17733         });
17734        */
17735         
17736          
17737        this.xtype = 'NavSimplebar';
17738         
17739         for(var i=0;i< children.length;i++) {
17740             
17741             this.buttons.add(this.addxtypeChild(children[i]));
17742             
17743         }
17744         
17745         editor.on('editorevent', this.updateToolbar, this);
17746     },
17747     onBtnClick : function(id)
17748     {
17749        this.editorcore.relayCmd(id);
17750        this.editorcore.focus();
17751     },
17752     
17753     /**
17754      * Protected method that will not generally be called directly. It triggers
17755      * a toolbar update by reading the markup state of the current selection in the editor.
17756      */
17757     updateToolbar: function(){
17758
17759         if(!this.editorcore.activated){
17760             this.editor.onFirstFocus(); // is this neeed?
17761             return;
17762         }
17763
17764         var btns = this.buttons; 
17765         var doc = this.editorcore.doc;
17766         btns.get('bold').setActive(doc.queryCommandState('bold'));
17767         btns.get('italic').setActive(doc.queryCommandState('italic'));
17768         //btns.get('underline').setActive(doc.queryCommandState('underline'));
17769         
17770         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
17771         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
17772         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
17773         
17774         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
17775         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
17776          /*
17777         
17778         var ans = this.editorcore.getAllAncestors();
17779         if (this.formatCombo) {
17780             
17781             
17782             var store = this.formatCombo.store;
17783             this.formatCombo.setValue("");
17784             for (var i =0; i < ans.length;i++) {
17785                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
17786                     // select it..
17787                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
17788                     break;
17789                 }
17790             }
17791         }
17792         
17793         
17794         
17795         // hides menus... - so this cant be on a menu...
17796         Roo.bootstrap.MenuMgr.hideAll();
17797         */
17798         Roo.bootstrap.MenuMgr.hideAll();
17799         //this.editorsyncValue();
17800     },
17801     onFirstFocus: function() {
17802         this.buttons.each(function(item){
17803            item.enable();
17804         });
17805     },
17806     toggleSourceEdit : function(sourceEditMode){
17807         
17808           
17809         if(sourceEditMode){
17810             Roo.log("disabling buttons");
17811            this.buttons.each( function(item){
17812                 if(item.cmd != 'pencil'){
17813                     item.disable();
17814                 }
17815             });
17816           
17817         }else{
17818             Roo.log("enabling buttons");
17819             if(this.editorcore.initialized){
17820                 this.buttons.each( function(item){
17821                     item.enable();
17822                 });
17823             }
17824             
17825         }
17826         Roo.log("calling toggole on editor");
17827         // tell the editor that it's been pressed..
17828         this.editor.toggleSourceEdit(sourceEditMode);
17829        
17830     }
17831 });
17832
17833
17834
17835
17836
17837 /**
17838  * @class Roo.bootstrap.Table.AbstractSelectionModel
17839  * @extends Roo.util.Observable
17840  * Abstract base class for grid SelectionModels.  It provides the interface that should be
17841  * implemented by descendant classes.  This class should not be directly instantiated.
17842  * @constructor
17843  */
17844 Roo.bootstrap.Table.AbstractSelectionModel = function(){
17845     this.locked = false;
17846     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
17847 };
17848
17849
17850 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
17851     /** @ignore Called by the grid automatically. Do not call directly. */
17852     init : function(grid){
17853         this.grid = grid;
17854         this.initEvents();
17855     },
17856
17857     /**
17858      * Locks the selections.
17859      */
17860     lock : function(){
17861         this.locked = true;
17862     },
17863
17864     /**
17865      * Unlocks the selections.
17866      */
17867     unlock : function(){
17868         this.locked = false;
17869     },
17870
17871     /**
17872      * Returns true if the selections are locked.
17873      * @return {Boolean}
17874      */
17875     isLocked : function(){
17876         return this.locked;
17877     }
17878 });
17879 /**
17880  * @extends Roo.bootstrap.Table.AbstractSelectionModel
17881  * @class Roo.bootstrap.Table.RowSelectionModel
17882  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
17883  * It supports multiple selections and keyboard selection/navigation. 
17884  * @constructor
17885  * @param {Object} config
17886  */
17887
17888 Roo.bootstrap.Table.RowSelectionModel = function(config){
17889     Roo.apply(this, config);
17890     this.selections = new Roo.util.MixedCollection(false, function(o){
17891         return o.id;
17892     });
17893
17894     this.last = false;
17895     this.lastActive = false;
17896
17897     this.addEvents({
17898         /**
17899              * @event selectionchange
17900              * Fires when the selection changes
17901              * @param {SelectionModel} this
17902              */
17903             "selectionchange" : true,
17904         /**
17905              * @event afterselectionchange
17906              * Fires after the selection changes (eg. by key press or clicking)
17907              * @param {SelectionModel} this
17908              */
17909             "afterselectionchange" : true,
17910         /**
17911              * @event beforerowselect
17912              * Fires when a row is selected being selected, return false to cancel.
17913              * @param {SelectionModel} this
17914              * @param {Number} rowIndex The selected index
17915              * @param {Boolean} keepExisting False if other selections will be cleared
17916              */
17917             "beforerowselect" : true,
17918         /**
17919              * @event rowselect
17920              * Fires when a row is selected.
17921              * @param {SelectionModel} this
17922              * @param {Number} rowIndex The selected index
17923              * @param {Roo.data.Record} r The record
17924              */
17925             "rowselect" : true,
17926         /**
17927              * @event rowdeselect
17928              * Fires when a row is deselected.
17929              * @param {SelectionModel} this
17930              * @param {Number} rowIndex The selected index
17931              */
17932         "rowdeselect" : true
17933     });
17934     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
17935     this.locked = false;
17936 };
17937
17938 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
17939     /**
17940      * @cfg {Boolean} singleSelect
17941      * True to allow selection of only one row at a time (defaults to false)
17942      */
17943     singleSelect : false,
17944
17945     // private
17946     initEvents : function(){
17947
17948         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
17949             this.grid.on("mousedown", this.handleMouseDown, this);
17950         }else{ // allow click to work like normal
17951             this.grid.on("rowclick", this.handleDragableRowClick, this);
17952         }
17953
17954         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
17955             "up" : function(e){
17956                 if(!e.shiftKey){
17957                     this.selectPrevious(e.shiftKey);
17958                 }else if(this.last !== false && this.lastActive !== false){
17959                     var last = this.last;
17960                     this.selectRange(this.last,  this.lastActive-1);
17961                     this.grid.getView().focusRow(this.lastActive);
17962                     if(last !== false){
17963                         this.last = last;
17964                     }
17965                 }else{
17966                     this.selectFirstRow();
17967                 }
17968                 this.fireEvent("afterselectionchange", this);
17969             },
17970             "down" : function(e){
17971                 if(!e.shiftKey){
17972                     this.selectNext(e.shiftKey);
17973                 }else if(this.last !== false && this.lastActive !== false){
17974                     var last = this.last;
17975                     this.selectRange(this.last,  this.lastActive+1);
17976                     this.grid.getView().focusRow(this.lastActive);
17977                     if(last !== false){
17978                         this.last = last;
17979                     }
17980                 }else{
17981                     this.selectFirstRow();
17982                 }
17983                 this.fireEvent("afterselectionchange", this);
17984             },
17985             scope: this
17986         });
17987
17988         var view = this.grid.view;
17989         view.on("refresh", this.onRefresh, this);
17990         view.on("rowupdated", this.onRowUpdated, this);
17991         view.on("rowremoved", this.onRemove, this);
17992     },
17993
17994     // private
17995     onRefresh : function(){
17996         var ds = this.grid.dataSource, i, v = this.grid.view;
17997         var s = this.selections;
17998         s.each(function(r){
17999             if((i = ds.indexOfId(r.id)) != -1){
18000                 v.onRowSelect(i);
18001             }else{
18002                 s.remove(r);
18003             }
18004         });
18005     },
18006
18007     // private
18008     onRemove : function(v, index, r){
18009         this.selections.remove(r);
18010     },
18011
18012     // private
18013     onRowUpdated : function(v, index, r){
18014         if(this.isSelected(r)){
18015             v.onRowSelect(index);
18016         }
18017     },
18018
18019     /**
18020      * Select records.
18021      * @param {Array} records The records to select
18022      * @param {Boolean} keepExisting (optional) True to keep existing selections
18023      */
18024     selectRecords : function(records, keepExisting){
18025         if(!keepExisting){
18026             this.clearSelections();
18027         }
18028         var ds = this.grid.dataSource;
18029         for(var i = 0, len = records.length; i < len; i++){
18030             this.selectRow(ds.indexOf(records[i]), true);
18031         }
18032     },
18033
18034     /**
18035      * Gets the number of selected rows.
18036      * @return {Number}
18037      */
18038     getCount : function(){
18039         return this.selections.length;
18040     },
18041
18042     /**
18043      * Selects the first row in the grid.
18044      */
18045     selectFirstRow : function(){
18046         this.selectRow(0);
18047     },
18048
18049     /**
18050      * Select the last row.
18051      * @param {Boolean} keepExisting (optional) True to keep existing selections
18052      */
18053     selectLastRow : function(keepExisting){
18054         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18055     },
18056
18057     /**
18058      * Selects the row immediately following the last selected row.
18059      * @param {Boolean} keepExisting (optional) True to keep existing selections
18060      */
18061     selectNext : function(keepExisting){
18062         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18063             this.selectRow(this.last+1, keepExisting);
18064             this.grid.getView().focusRow(this.last);
18065         }
18066     },
18067
18068     /**
18069      * Selects the row that precedes the last selected row.
18070      * @param {Boolean} keepExisting (optional) True to keep existing selections
18071      */
18072     selectPrevious : function(keepExisting){
18073         if(this.last){
18074             this.selectRow(this.last-1, keepExisting);
18075             this.grid.getView().focusRow(this.last);
18076         }
18077     },
18078
18079     /**
18080      * Returns the selected records
18081      * @return {Array} Array of selected records
18082      */
18083     getSelections : function(){
18084         return [].concat(this.selections.items);
18085     },
18086
18087     /**
18088      * Returns the first selected record.
18089      * @return {Record}
18090      */
18091     getSelected : function(){
18092         return this.selections.itemAt(0);
18093     },
18094
18095
18096     /**
18097      * Clears all selections.
18098      */
18099     clearSelections : function(fast){
18100         if(this.locked) return;
18101         if(fast !== true){
18102             var ds = this.grid.dataSource;
18103             var s = this.selections;
18104             s.each(function(r){
18105                 this.deselectRow(ds.indexOfId(r.id));
18106             }, this);
18107             s.clear();
18108         }else{
18109             this.selections.clear();
18110         }
18111         this.last = false;
18112     },
18113
18114
18115     /**
18116      * Selects all rows.
18117      */
18118     selectAll : function(){
18119         if(this.locked) return;
18120         this.selections.clear();
18121         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18122             this.selectRow(i, true);
18123         }
18124     },
18125
18126     /**
18127      * Returns True if there is a selection.
18128      * @return {Boolean}
18129      */
18130     hasSelection : function(){
18131         return this.selections.length > 0;
18132     },
18133
18134     /**
18135      * Returns True if the specified row is selected.
18136      * @param {Number/Record} record The record or index of the record to check
18137      * @return {Boolean}
18138      */
18139     isSelected : function(index){
18140         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18141         return (r && this.selections.key(r.id) ? true : false);
18142     },
18143
18144     /**
18145      * Returns True if the specified record id is selected.
18146      * @param {String} id The id of record to check
18147      * @return {Boolean}
18148      */
18149     isIdSelected : function(id){
18150         return (this.selections.key(id) ? true : false);
18151     },
18152
18153     // private
18154     handleMouseDown : function(e, t){
18155         var view = this.grid.getView(), rowIndex;
18156         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18157             return;
18158         };
18159         if(e.shiftKey && this.last !== false){
18160             var last = this.last;
18161             this.selectRange(last, rowIndex, e.ctrlKey);
18162             this.last = last; // reset the last
18163             view.focusRow(rowIndex);
18164         }else{
18165             var isSelected = this.isSelected(rowIndex);
18166             if(e.button !== 0 && isSelected){
18167                 view.focusRow(rowIndex);
18168             }else if(e.ctrlKey && isSelected){
18169                 this.deselectRow(rowIndex);
18170             }else if(!isSelected){
18171                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18172                 view.focusRow(rowIndex);
18173             }
18174         }
18175         this.fireEvent("afterselectionchange", this);
18176     },
18177     // private
18178     handleDragableRowClick :  function(grid, rowIndex, e) 
18179     {
18180         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18181             this.selectRow(rowIndex, false);
18182             grid.view.focusRow(rowIndex);
18183              this.fireEvent("afterselectionchange", this);
18184         }
18185     },
18186     
18187     /**
18188      * Selects multiple rows.
18189      * @param {Array} rows Array of the indexes of the row to select
18190      * @param {Boolean} keepExisting (optional) True to keep existing selections
18191      */
18192     selectRows : function(rows, keepExisting){
18193         if(!keepExisting){
18194             this.clearSelections();
18195         }
18196         for(var i = 0, len = rows.length; i < len; i++){
18197             this.selectRow(rows[i], true);
18198         }
18199     },
18200
18201     /**
18202      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18203      * @param {Number} startRow The index of the first row in the range
18204      * @param {Number} endRow The index of the last row in the range
18205      * @param {Boolean} keepExisting (optional) True to retain existing selections
18206      */
18207     selectRange : function(startRow, endRow, keepExisting){
18208         if(this.locked) return;
18209         if(!keepExisting){
18210             this.clearSelections();
18211         }
18212         if(startRow <= endRow){
18213             for(var i = startRow; i <= endRow; i++){
18214                 this.selectRow(i, true);
18215             }
18216         }else{
18217             for(var i = startRow; i >= endRow; i--){
18218                 this.selectRow(i, true);
18219             }
18220         }
18221     },
18222
18223     /**
18224      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18225      * @param {Number} startRow The index of the first row in the range
18226      * @param {Number} endRow The index of the last row in the range
18227      */
18228     deselectRange : function(startRow, endRow, preventViewNotify){
18229         if(this.locked) return;
18230         for(var i = startRow; i <= endRow; i++){
18231             this.deselectRow(i, preventViewNotify);
18232         }
18233     },
18234
18235     /**
18236      * Selects a row.
18237      * @param {Number} row The index of the row to select
18238      * @param {Boolean} keepExisting (optional) True to keep existing selections
18239      */
18240     selectRow : function(index, keepExisting, preventViewNotify){
18241         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18242         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18243             if(!keepExisting || this.singleSelect){
18244                 this.clearSelections();
18245             }
18246             var r = this.grid.dataSource.getAt(index);
18247             this.selections.add(r);
18248             this.last = this.lastActive = index;
18249             if(!preventViewNotify){
18250                 this.grid.getView().onRowSelect(index);
18251             }
18252             this.fireEvent("rowselect", this, index, r);
18253             this.fireEvent("selectionchange", this);
18254         }
18255     },
18256
18257     /**
18258      * Deselects a row.
18259      * @param {Number} row The index of the row to deselect
18260      */
18261     deselectRow : function(index, preventViewNotify){
18262         if(this.locked) return;
18263         if(this.last == index){
18264             this.last = false;
18265         }
18266         if(this.lastActive == index){
18267             this.lastActive = false;
18268         }
18269         var r = this.grid.dataSource.getAt(index);
18270         this.selections.remove(r);
18271         if(!preventViewNotify){
18272             this.grid.getView().onRowDeselect(index);
18273         }
18274         this.fireEvent("rowdeselect", this, index);
18275         this.fireEvent("selectionchange", this);
18276     },
18277
18278     // private
18279     restoreLast : function(){
18280         if(this._last){
18281             this.last = this._last;
18282         }
18283     },
18284
18285     // private
18286     acceptsNav : function(row, col, cm){
18287         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18288     },
18289
18290     // private
18291     onEditorKey : function(field, e){
18292         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18293         if(k == e.TAB){
18294             e.stopEvent();
18295             ed.completeEdit();
18296             if(e.shiftKey){
18297                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18298             }else{
18299                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18300             }
18301         }else if(k == e.ENTER && !e.ctrlKey){
18302             e.stopEvent();
18303             ed.completeEdit();
18304             if(e.shiftKey){
18305                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18306             }else{
18307                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18308             }
18309         }else if(k == e.ESC){
18310             ed.cancelEdit();
18311         }
18312         if(newCell){
18313             g.startEditing(newCell[0], newCell[1]);
18314         }
18315     }
18316 });/*
18317  * Based on:
18318  * Ext JS Library 1.1.1
18319  * Copyright(c) 2006-2007, Ext JS, LLC.
18320  *
18321  * Originally Released Under LGPL - original licence link has changed is not relivant.
18322  *
18323  * Fork - LGPL
18324  * <script type="text/javascript">
18325  */
18326  
18327 /**
18328  * @class Roo.bootstrap.PagingToolbar
18329  * @extends Roo.Row
18330  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18331  * @constructor
18332  * Create a new PagingToolbar
18333  * @param {Object} config The config object
18334  */
18335 Roo.bootstrap.PagingToolbar = function(config)
18336 {
18337     // old args format still supported... - xtype is prefered..
18338         // created from xtype...
18339     var ds = config.dataSource;
18340     this.toolbarItems = [];
18341     if (config.items) {
18342         this.toolbarItems = config.items;
18343 //        config.items = [];
18344     }
18345     
18346     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18347     this.ds = ds;
18348     this.cursor = 0;
18349     if (ds) { 
18350         this.bind(ds);
18351     }
18352     
18353     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18354     
18355 };
18356
18357 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18358     /**
18359      * @cfg {Roo.data.Store} dataSource
18360      * The underlying data store providing the paged data
18361      */
18362     /**
18363      * @cfg {String/HTMLElement/Element} container
18364      * container The id or element that will contain the toolbar
18365      */
18366     /**
18367      * @cfg {Boolean} displayInfo
18368      * True to display the displayMsg (defaults to false)
18369      */
18370     /**
18371      * @cfg {Number} pageSize
18372      * The number of records to display per page (defaults to 20)
18373      */
18374     pageSize: 20,
18375     /**
18376      * @cfg {String} displayMsg
18377      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18378      */
18379     displayMsg : 'Displaying {0} - {1} of {2}',
18380     /**
18381      * @cfg {String} emptyMsg
18382      * The message to display when no records are found (defaults to "No data to display")
18383      */
18384     emptyMsg : 'No data to display',
18385     /**
18386      * Customizable piece of the default paging text (defaults to "Page")
18387      * @type String
18388      */
18389     beforePageText : "Page",
18390     /**
18391      * Customizable piece of the default paging text (defaults to "of %0")
18392      * @type String
18393      */
18394     afterPageText : "of {0}",
18395     /**
18396      * Customizable piece of the default paging text (defaults to "First Page")
18397      * @type String
18398      */
18399     firstText : "First Page",
18400     /**
18401      * Customizable piece of the default paging text (defaults to "Previous Page")
18402      * @type String
18403      */
18404     prevText : "Previous Page",
18405     /**
18406      * Customizable piece of the default paging text (defaults to "Next Page")
18407      * @type String
18408      */
18409     nextText : "Next Page",
18410     /**
18411      * Customizable piece of the default paging text (defaults to "Last Page")
18412      * @type String
18413      */
18414     lastText : "Last Page",
18415     /**
18416      * Customizable piece of the default paging text (defaults to "Refresh")
18417      * @type String
18418      */
18419     refreshText : "Refresh",
18420
18421     buttons : false,
18422     // private
18423     onRender : function(ct, position) 
18424     {
18425         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18426         this.navgroup.parentId = this.id;
18427         this.navgroup.onRender(this.el, null);
18428         // add the buttons to the navgroup
18429         
18430         if(this.displayInfo){
18431             Roo.log(this.el.select('ul.navbar-nav',true).first());
18432             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18433             this.displayEl = this.el.select('.x-paging-info', true).first();
18434 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18435 //            this.displayEl = navel.el.select('span',true).first();
18436         }
18437         
18438         var _this = this;
18439         
18440         if(this.buttons){
18441             Roo.each(_this.buttons, function(e){
18442                Roo.factory(e).onRender(_this.el, null);
18443             });
18444         }
18445             
18446         Roo.each(_this.toolbarItems, function(e) {
18447             _this.navgroup.addItem(e);
18448         });
18449         
18450         this.first = this.navgroup.addItem({
18451             tooltip: this.firstText,
18452             cls: "prev",
18453             icon : 'fa fa-backward',
18454             disabled: true,
18455             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
18456         });
18457         
18458         this.prev =  this.navgroup.addItem({
18459             tooltip: this.prevText,
18460             cls: "prev",
18461             icon : 'fa fa-step-backward',
18462             disabled: true,
18463             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
18464         });
18465     //this.addSeparator();
18466         
18467         
18468         var field = this.navgroup.addItem( {
18469             tagtype : 'span',
18470             cls : 'x-paging-position',
18471             
18472             html : this.beforePageText  +
18473                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
18474                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
18475          } ); //?? escaped?
18476         
18477         this.field = field.el.select('input', true).first();
18478         this.field.on("keydown", this.onPagingKeydown, this);
18479         this.field.on("focus", function(){this.dom.select();});
18480     
18481     
18482         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
18483         //this.field.setHeight(18);
18484         //this.addSeparator();
18485         this.next = this.navgroup.addItem({
18486             tooltip: this.nextText,
18487             cls: "next",
18488             html : ' <i class="fa fa-step-forward">',
18489             disabled: true,
18490             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
18491         });
18492         this.last = this.navgroup.addItem({
18493             tooltip: this.lastText,
18494             icon : 'fa fa-forward',
18495             cls: "next",
18496             disabled: true,
18497             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
18498         });
18499     //this.addSeparator();
18500         this.loading = this.navgroup.addItem({
18501             tooltip: this.refreshText,
18502             icon: 'fa fa-refresh',
18503             
18504             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
18505         });
18506
18507     },
18508
18509     // private
18510     updateInfo : function(){
18511         if(this.displayEl){
18512             var count = this.ds.getCount();
18513             var msg = count == 0 ?
18514                 this.emptyMsg :
18515                 String.format(
18516                     this.displayMsg,
18517                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
18518                 );
18519             this.displayEl.update(msg);
18520         }
18521     },
18522
18523     // private
18524     onLoad : function(ds, r, o){
18525        this.cursor = o.params ? o.params.start : 0;
18526        var d = this.getPageData(),
18527             ap = d.activePage,
18528             ps = d.pages;
18529         
18530        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
18531        this.field.dom.value = ap;
18532        this.first.setDisabled(ap == 1);
18533        this.prev.setDisabled(ap == 1);
18534        this.next.setDisabled(ap == ps);
18535        this.last.setDisabled(ap == ps);
18536        this.loading.enable();
18537        this.updateInfo();
18538     },
18539
18540     // private
18541     getPageData : function(){
18542         var total = this.ds.getTotalCount();
18543         return {
18544             total : total,
18545             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
18546             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
18547         };
18548     },
18549
18550     // private
18551     onLoadError : function(){
18552         this.loading.enable();
18553     },
18554
18555     // private
18556     onPagingKeydown : function(e){
18557         var k = e.getKey();
18558         var d = this.getPageData();
18559         if(k == e.RETURN){
18560             var v = this.field.dom.value, pageNum;
18561             if(!v || isNaN(pageNum = parseInt(v, 10))){
18562                 this.field.dom.value = d.activePage;
18563                 return;
18564             }
18565             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
18566             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18567             e.stopEvent();
18568         }
18569         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))
18570         {
18571           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
18572           this.field.dom.value = pageNum;
18573           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
18574           e.stopEvent();
18575         }
18576         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18577         {
18578           var v = this.field.dom.value, pageNum; 
18579           var increment = (e.shiftKey) ? 10 : 1;
18580           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18581             increment *= -1;
18582           if(!v || isNaN(pageNum = parseInt(v, 10))) {
18583             this.field.dom.value = d.activePage;
18584             return;
18585           }
18586           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
18587           {
18588             this.field.dom.value = parseInt(v, 10) + increment;
18589             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
18590             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18591           }
18592           e.stopEvent();
18593         }
18594     },
18595
18596     // private
18597     beforeLoad : function(){
18598         if(this.loading){
18599             this.loading.disable();
18600         }
18601     },
18602
18603     // private
18604     onClick : function(which){
18605         var ds = this.ds;
18606         if (!ds) {
18607             return;
18608         }
18609         switch(which){
18610             case "first":
18611                 ds.load({params:{start: 0, limit: this.pageSize}});
18612             break;
18613             case "prev":
18614                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
18615             break;
18616             case "next":
18617                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
18618             break;
18619             case "last":
18620                 var total = ds.getTotalCount();
18621                 var extra = total % this.pageSize;
18622                 var lastStart = extra ? (total - extra) : total-this.pageSize;
18623                 ds.load({params:{start: lastStart, limit: this.pageSize}});
18624             break;
18625             case "refresh":
18626                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
18627             break;
18628         }
18629     },
18630
18631     /**
18632      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
18633      * @param {Roo.data.Store} store The data store to unbind
18634      */
18635     unbind : function(ds){
18636         ds.un("beforeload", this.beforeLoad, this);
18637         ds.un("load", this.onLoad, this);
18638         ds.un("loadexception", this.onLoadError, this);
18639         ds.un("remove", this.updateInfo, this);
18640         ds.un("add", this.updateInfo, this);
18641         this.ds = undefined;
18642     },
18643
18644     /**
18645      * Binds the paging toolbar to the specified {@link Roo.data.Store}
18646      * @param {Roo.data.Store} store The data store to bind
18647      */
18648     bind : function(ds){
18649         ds.on("beforeload", this.beforeLoad, this);
18650         ds.on("load", this.onLoad, this);
18651         ds.on("loadexception", this.onLoadError, this);
18652         ds.on("remove", this.updateInfo, this);
18653         ds.on("add", this.updateInfo, this);
18654         this.ds = ds;
18655     }
18656 });/*
18657  * - LGPL
18658  *
18659  * element
18660  * 
18661  */
18662
18663 /**
18664  * @class Roo.bootstrap.MessageBar
18665  * @extends Roo.bootstrap.Component
18666  * Bootstrap MessageBar class
18667  * @cfg {String} html contents of the MessageBar
18668  * @cfg {String} weight (info | success | warning | danger) default info
18669  * @cfg {String} beforeClass insert the bar before the given class
18670  * @cfg {Boolean} closable (true | false) default false
18671  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
18672  * 
18673  * @constructor
18674  * Create a new Element
18675  * @param {Object} config The config object
18676  */
18677
18678 Roo.bootstrap.MessageBar = function(config){
18679     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
18680 };
18681
18682 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
18683     
18684     html: '',
18685     weight: 'info',
18686     closable: false,
18687     fixed: false,
18688     beforeClass: 'bootstrap-sticky-wrap',
18689     
18690     getAutoCreate : function(){
18691         
18692         var cfg = {
18693             tag: 'div',
18694             cls: 'alert alert-dismissable alert-' + this.weight,
18695             cn: [
18696                 {
18697                     tag: 'span',
18698                     cls: 'message',
18699                     html: this.html || ''
18700                 }
18701             ]
18702         }
18703         
18704         if(this.fixed){
18705             cfg.cls += ' alert-messages-fixed';
18706         }
18707         
18708         if(this.closable){
18709             cfg.cn.push({
18710                 tag: 'button',
18711                 cls: 'close',
18712                 html: 'x'
18713             });
18714         }
18715         
18716         return cfg;
18717     },
18718     
18719     onRender : function(ct, position)
18720     {
18721         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18722         
18723         if(!this.el){
18724             var cfg = Roo.apply({},  this.getAutoCreate());
18725             cfg.id = Roo.id();
18726             
18727             if (this.cls) {
18728                 cfg.cls += ' ' + this.cls;
18729             }
18730             if (this.style) {
18731                 cfg.style = this.style;
18732             }
18733             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
18734             
18735             this.el.setVisibilityMode(Roo.Element.DISPLAY);
18736         }
18737         
18738         this.el.select('>button.close').on('click', this.hide, this);
18739         
18740     },
18741     
18742     show : function()
18743     {
18744         if (!this.rendered) {
18745             this.render();
18746         }
18747         
18748         this.el.show();
18749         
18750         this.fireEvent('show', this);
18751         
18752     },
18753     
18754     hide : function()
18755     {
18756         if (!this.rendered) {
18757             this.render();
18758         }
18759         
18760         this.el.hide();
18761         
18762         this.fireEvent('hide', this);
18763     },
18764     
18765     update : function()
18766     {
18767 //        var e = this.el.dom.firstChild;
18768 //        
18769 //        if(this.closable){
18770 //            e = e.nextSibling;
18771 //        }
18772 //        
18773 //        e.data = this.html || '';
18774
18775         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
18776     }
18777    
18778 });
18779
18780  
18781
18782      /*
18783  * - LGPL
18784  *
18785  * Graph
18786  * 
18787  */
18788
18789
18790 /**
18791  * @class Roo.bootstrap.Graph
18792  * @extends Roo.bootstrap.Component
18793  * Bootstrap Graph class
18794 > Prameters
18795  -sm {number} sm 4
18796  -md {number} md 5
18797  @cfg {String} graphtype  bar | vbar | pie
18798  @cfg {number} g_x coodinator | centre x (pie)
18799  @cfg {number} g_y coodinator | centre y (pie)
18800  @cfg {number} g_r radius (pie)
18801  @cfg {number} g_height height of the chart (respected by all elements in the set)
18802  @cfg {number} g_width width of the chart (respected by all elements in the set)
18803  @cfg {Object} title The title of the chart
18804     
18805  -{Array}  values
18806  -opts (object) options for the chart 
18807      o {
18808      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
18809      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
18810      o vgutter (number)
18811      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.
18812      o stacked (boolean) whether or not to tread values as in a stacked bar chart
18813      o to
18814      o stretch (boolean)
18815      o }
18816  -opts (object) options for the pie
18817      o{
18818      o cut
18819      o startAngle (number)
18820      o endAngle (number)
18821      } 
18822  *
18823  * @constructor
18824  * Create a new Input
18825  * @param {Object} config The config object
18826  */
18827
18828 Roo.bootstrap.Graph = function(config){
18829     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
18830     
18831     this.addEvents({
18832         // img events
18833         /**
18834          * @event click
18835          * The img click event for the img.
18836          * @param {Roo.EventObject} e
18837          */
18838         "click" : true
18839     });
18840 };
18841
18842 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
18843     
18844     sm: 4,
18845     md: 5,
18846     graphtype: 'bar',
18847     g_height: 250,
18848     g_width: 400,
18849     g_x: 50,
18850     g_y: 50,
18851     g_r: 30,
18852     opts:{
18853         //g_colors: this.colors,
18854         g_type: 'soft',
18855         g_gutter: '20%'
18856
18857     },
18858     title : false,
18859
18860     getAutoCreate : function(){
18861         
18862         var cfg = {
18863             tag: 'div',
18864             html : null
18865         }
18866         
18867         
18868         return  cfg;
18869     },
18870
18871     onRender : function(ct,position){
18872         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
18873         this.raphael = Raphael(this.el.dom);
18874         
18875                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
18876                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
18877                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
18878                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
18879                 /*
18880                 r.text(160, 10, "Single Series Chart").attr(txtattr);
18881                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
18882                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
18883                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
18884                 
18885                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
18886                 r.barchart(330, 10, 300, 220, data1);
18887                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
18888                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
18889                 */
18890                 
18891                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
18892                 // r.barchart(30, 30, 560, 250,  xdata, {
18893                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
18894                 //     axis : "0 0 1 1",
18895                 //     axisxlabels :  xdata
18896                 //     //yvalues : cols,
18897                    
18898                 // });
18899 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
18900 //        
18901 //        this.load(null,xdata,{
18902 //                axis : "0 0 1 1",
18903 //                axisxlabels :  xdata
18904 //                });
18905
18906     },
18907
18908     load : function(graphtype,xdata,opts){
18909         this.raphael.clear();
18910         if(!graphtype) {
18911             graphtype = this.graphtype;
18912         }
18913         if(!opts){
18914             opts = this.opts;
18915         }
18916         var r = this.raphael,
18917             fin = function () {
18918                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
18919             },
18920             fout = function () {
18921                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
18922             },
18923             pfin = function() {
18924                 this.sector.stop();
18925                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
18926
18927                 if (this.label) {
18928                     this.label[0].stop();
18929                     this.label[0].attr({ r: 7.5 });
18930                     this.label[1].attr({ "font-weight": 800 });
18931                 }
18932             },
18933             pfout = function() {
18934                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
18935
18936                 if (this.label) {
18937                     this.label[0].animate({ r: 5 }, 500, "bounce");
18938                     this.label[1].attr({ "font-weight": 400 });
18939                 }
18940             };
18941
18942         switch(graphtype){
18943             case 'bar':
18944                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
18945                 break;
18946             case 'hbar':
18947                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
18948                 break;
18949             case 'pie':
18950 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
18951 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
18952 //            
18953                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
18954                 
18955                 break;
18956
18957         }
18958         
18959         if(this.title){
18960             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
18961         }
18962         
18963     },
18964     
18965     setTitle: function(o)
18966     {
18967         this.title = o;
18968     },
18969     
18970     initEvents: function() {
18971         
18972         if(!this.href){
18973             this.el.on('click', this.onClick, this);
18974         }
18975     },
18976     
18977     onClick : function(e)
18978     {
18979         Roo.log('img onclick');
18980         this.fireEvent('click', this, e);
18981     }
18982    
18983 });
18984
18985  
18986 /*
18987  * - LGPL
18988  *
18989  * numberBox
18990  * 
18991  */
18992 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
18993
18994 /**
18995  * @class Roo.bootstrap.dash.NumberBox
18996  * @extends Roo.bootstrap.Component
18997  * Bootstrap NumberBox class
18998  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
18999  * @cfg {String} headline Box headline
19000  * @cfg {String} content Box content
19001  * @cfg {String} icon Box icon
19002  * @cfg {String} footer Footer text
19003  * @cfg {String} fhref Footer href
19004  * 
19005  * @constructor
19006  * Create a new NumberBox
19007  * @param {Object} config The config object
19008  */
19009
19010
19011 Roo.bootstrap.dash.NumberBox = function(config){
19012     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19013     
19014 };
19015
19016 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19017     
19018     bgcolor : 'aqua',
19019     headline : '',
19020     content : '',
19021     icon : '',
19022     footer : '',
19023     fhref : '',
19024     ficon : '',
19025     
19026     getAutoCreate : function(){
19027         
19028         var cfg = {
19029             tag : 'div',
19030             cls : 'small-box bg-' + this.bgcolor,
19031             cn : [
19032                 {
19033                     tag : 'div',
19034                     cls : 'inner',
19035                     cn :[
19036                         {
19037                             tag : 'h3',
19038                             cls : 'roo-headline',
19039                             html : this.headline
19040                         },
19041                         {
19042                             tag : 'p',
19043                             cls : 'roo-content',
19044                             html : this.content
19045                         }
19046                     ]
19047                 }
19048             ]
19049         }
19050         
19051         if(this.icon){
19052             cfg.cn.push({
19053                 tag : 'div',
19054                 cls : 'icon',
19055                 cn :[
19056                     {
19057                         tag : 'i',
19058                         cls : 'ion ' + this.icon
19059                     }
19060                 ]
19061             });
19062         }
19063         
19064         if(this.footer){
19065             var footer = {
19066                 tag : 'a',
19067                 cls : 'small-box-footer',
19068                 href : this.fhref || '#',
19069                 html : this.footer
19070             };
19071             
19072             cfg.cn.push(footer);
19073             
19074         }
19075         
19076         return  cfg;
19077     },
19078
19079     onRender : function(ct,position){
19080         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19081
19082
19083        
19084                 
19085     },
19086
19087     setHeadline: function (value)
19088     {
19089         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19090     },
19091     
19092     setFooter: function (value, href)
19093     {
19094         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19095         
19096         if(href){
19097             this.el.select('a.small-box-footer',true).first().attr('href', href);
19098         }
19099         
19100     },
19101
19102     setContent: function (value)
19103     {
19104         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19105     },
19106
19107     initEvents: function() 
19108     {   
19109         
19110     }
19111     
19112 });
19113
19114  
19115 /*
19116  * - LGPL
19117  *
19118  * TabBox
19119  * 
19120  */
19121 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19122
19123 /**
19124  * @class Roo.bootstrap.dash.TabBox
19125  * @extends Roo.bootstrap.Component
19126  * Bootstrap TabBox class
19127  * @cfg {String} title Title of the TabBox
19128  * @cfg {String} icon Icon of the TabBox
19129  * 
19130  * @constructor
19131  * Create a new TabBox
19132  * @param {Object} config The config object
19133  */
19134
19135
19136 Roo.bootstrap.dash.TabBox = function(config){
19137     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19138     this.addEvents({
19139         // raw events
19140         /**
19141          * @event addpane
19142          * When a pane is added
19143          * @param {Roo.bootstrap.dash.TabPane} pane
19144          */
19145         "addpane" : true
19146          
19147     });
19148 };
19149
19150 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19151
19152     title : '',
19153     icon : false,
19154     
19155     getChildContainer : function()
19156     {
19157         return this.el.select('.tab-content', true).first();
19158     },
19159     
19160     getAutoCreate : function(){
19161         
19162         var header = {
19163             tag: 'li',
19164             cls: 'pull-left header',
19165             html: this.title,
19166             cn : []
19167         };
19168         
19169         if(this.icon){
19170             header.cn.push({
19171                 tag: 'i',
19172                 cls: 'fa ' + this.icon
19173             });
19174         }
19175         
19176         
19177         var cfg = {
19178             tag: 'div',
19179             cls: 'nav-tabs-custom',
19180             cn: [
19181                 {
19182                     tag: 'ul',
19183                     cls: 'nav nav-tabs pull-right',
19184                     cn: [
19185                         header
19186                     ]
19187                 },
19188                 {
19189                     tag: 'div',
19190                     cls: 'tab-content no-padding',
19191                     cn: []
19192                 }
19193             ]
19194         }
19195
19196         return  cfg;
19197     },
19198     initEvents : function()
19199     {
19200         //Roo.log('add add pane handler');
19201         this.on('addpane', this.onAddPane, this);
19202     },
19203      /**
19204      * Updates the box title
19205      * @param {String} html to set the title to.
19206      */
19207     setTitle : function(value)
19208     {
19209         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19210     },
19211     onAddPane : function(pane)
19212     {
19213         //Roo.log('addpane');
19214         //Roo.log(pane);
19215         // tabs are rendere left to right..
19216         var ctr = this.el.select('.nav-tabs', true).first();
19217          
19218          
19219         var existing = ctr.select('.nav-tab',true);
19220         var qty = existing.getCount();;
19221         
19222         
19223         var tab = ctr.createChild({
19224             tag : 'li',
19225             cls : 'nav-tab' + (qty ? '' : ' active'),
19226             cn : [
19227                 {
19228                     tag : 'a',
19229                     href:'#',
19230                     html : pane.title
19231                 }
19232             ]
19233         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19234         pane.tab = tab;
19235         
19236         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19237         if (!qty) {
19238             pane.el.addClass('active');
19239         }
19240         
19241                 
19242     },
19243     onTabClick : function(ev,un,ob,pane)
19244     {
19245         //Roo.log('tab - prev default');
19246         ev.preventDefault();
19247         
19248         
19249         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19250         pane.tab.addClass('active');
19251         //Roo.log(pane.title);
19252         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19253         // technically we should have a deactivate event.. but maybe add later.
19254         // and it should not de-activate the selected tab...
19255         
19256         pane.el.addClass('active');
19257         pane.fireEvent('activate');
19258         
19259         
19260     }
19261     
19262     
19263 });
19264
19265  
19266 /*
19267  * - LGPL
19268  *
19269  * Tab pane
19270  * 
19271  */
19272 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19273 /**
19274  * @class Roo.bootstrap.TabPane
19275  * @extends Roo.bootstrap.Component
19276  * Bootstrap TabPane class
19277  * @cfg {Boolean} active (false | true) Default false
19278  * @cfg {String} title title of panel
19279
19280  * 
19281  * @constructor
19282  * Create a new TabPane
19283  * @param {Object} config The config object
19284  */
19285
19286 Roo.bootstrap.dash.TabPane = function(config){
19287     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19288     
19289 };
19290
19291 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19292     
19293     active : false,
19294     title : '',
19295     
19296     // the tabBox that this is attached to.
19297     tab : false,
19298      
19299     getAutoCreate : function() 
19300     {
19301         var cfg = {
19302             tag: 'div',
19303             cls: 'tab-pane'
19304         }
19305         
19306         if(this.active){
19307             cfg.cls += ' active';
19308         }
19309         
19310         return cfg;
19311     },
19312     initEvents  : function()
19313     {
19314         //Roo.log('trigger add pane handler');
19315         this.parent().fireEvent('addpane', this)
19316     },
19317     
19318      /**
19319      * Updates the tab title 
19320      * @param {String} html to set the title to.
19321      */
19322     setTitle: function(str)
19323     {
19324         if (!this.tab) {
19325             return;
19326         }
19327         this.title = str;
19328         this.tab.select('a'.true).first().dom.innerHTML = str;
19329         
19330     }
19331     
19332     
19333     
19334 });
19335
19336  
19337
19338
19339  /*
19340  * - LGPL
19341  *
19342  * menu
19343  * 
19344  */
19345 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19346
19347 /**
19348  * @class Roo.bootstrap.menu.Menu
19349  * @extends Roo.bootstrap.Component
19350  * Bootstrap Menu class - container for Menu
19351  * @cfg {String} html Text of the menu
19352  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19353  * @cfg {String} icon Font awesome icon
19354  * @cfg {String} pos Menu align to (top | bottom) default bottom
19355  * 
19356  * 
19357  * @constructor
19358  * Create a new Menu
19359  * @param {Object} config The config object
19360  */
19361
19362
19363 Roo.bootstrap.menu.Menu = function(config){
19364     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19365     
19366     this.addEvents({
19367         /**
19368          * @event beforeshow
19369          * Fires before this menu is displayed
19370          * @param {Roo.bootstrap.menu.Menu} this
19371          */
19372         beforeshow : true,
19373         /**
19374          * @event beforehide
19375          * Fires before this menu is hidden
19376          * @param {Roo.bootstrap.menu.Menu} this
19377          */
19378         beforehide : true,
19379         /**
19380          * @event show
19381          * Fires after this menu is displayed
19382          * @param {Roo.bootstrap.menu.Menu} this
19383          */
19384         show : true,
19385         /**
19386          * @event hide
19387          * Fires after this menu is hidden
19388          * @param {Roo.bootstrap.menu.Menu} this
19389          */
19390         hide : true,
19391         /**
19392          * @event click
19393          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19394          * @param {Roo.bootstrap.menu.Menu} this
19395          * @param {Roo.EventObject} e
19396          */
19397         click : true
19398     });
19399     
19400 };
19401
19402 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19403     
19404     submenu : false,
19405     html : '',
19406     weight : 'default',
19407     icon : false,
19408     pos : 'bottom',
19409     
19410     
19411     getChildContainer : function() {
19412         if(this.isSubMenu){
19413             return this.el;
19414         }
19415         
19416         return this.el.select('ul.dropdown-menu', true).first();  
19417     },
19418     
19419     getAutoCreate : function()
19420     {
19421         var text = [
19422             {
19423                 tag : 'span',
19424                 cls : 'roo-menu-text',
19425                 html : this.html
19426             }
19427         ];
19428         
19429         if(this.icon){
19430             text.unshift({
19431                 tag : 'i',
19432                 cls : 'fa ' + this.icon
19433             })
19434         }
19435         
19436         
19437         var cfg = {
19438             tag : 'div',
19439             cls : 'btn-group',
19440             cn : [
19441                 {
19442                     tag : 'button',
19443                     cls : 'dropdown-button btn btn-' + this.weight,
19444                     cn : text
19445                 },
19446                 {
19447                     tag : 'button',
19448                     cls : 'dropdown-toggle btn btn-' + this.weight,
19449                     cn : [
19450                         {
19451                             tag : 'span',
19452                             cls : 'caret'
19453                         }
19454                     ]
19455                 },
19456                 {
19457                     tag : 'ul',
19458                     cls : 'dropdown-menu'
19459                 }
19460             ]
19461             
19462         };
19463         
19464         if(this.pos == 'top'){
19465             cfg.cls += ' dropup';
19466         }
19467         
19468         if(this.isSubMenu){
19469             cfg = {
19470                 tag : 'ul',
19471                 cls : 'dropdown-menu'
19472             }
19473         }
19474         
19475         return cfg;
19476     },
19477     
19478     onRender : function(ct, position)
19479     {
19480         this.isSubMenu = ct.hasClass('dropdown-submenu');
19481         
19482         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
19483     },
19484     
19485     initEvents : function() 
19486     {
19487         if(this.isSubMenu){
19488             return;
19489         }
19490         
19491         this.hidden = true;
19492         
19493         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
19494         this.triggerEl.on('click', this.onTriggerPress, this);
19495         
19496         this.buttonEl = this.el.select('button.dropdown-button', true).first();
19497         this.buttonEl.on('click', this.onClick, this);
19498         
19499     },
19500     
19501     list : function()
19502     {
19503         if(this.isSubMenu){
19504             return this.el;
19505         }
19506         
19507         return this.el.select('ul.dropdown-menu', true).first();
19508     },
19509     
19510     onClick : function(e)
19511     {
19512         this.fireEvent("click", this, e);
19513     },
19514     
19515     onTriggerPress  : function(e)
19516     {   
19517         if (this.isVisible()) {
19518             this.hide();
19519         } else {
19520             this.show();
19521         }
19522     },
19523     
19524     isVisible : function(){
19525         return !this.hidden;
19526     },
19527     
19528     show : function()
19529     {
19530         this.fireEvent("beforeshow", this);
19531         
19532         this.hidden = false;
19533         this.el.addClass('open');
19534         
19535         Roo.get(document).on("mouseup", this.onMouseUp, this);
19536         
19537         this.fireEvent("show", this);
19538         
19539         
19540     },
19541     
19542     hide : function()
19543     {
19544         this.fireEvent("beforehide", this);
19545         
19546         this.hidden = true;
19547         this.el.removeClass('open');
19548         
19549         Roo.get(document).un("mouseup", this.onMouseUp);
19550         
19551         this.fireEvent("hide", this);
19552     },
19553     
19554     onMouseUp : function()
19555     {
19556         this.hide();
19557     }
19558     
19559 });
19560
19561  
19562  /*
19563  * - LGPL
19564  *
19565  * menu item
19566  * 
19567  */
19568 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19569
19570 /**
19571  * @class Roo.bootstrap.menu.Item
19572  * @extends Roo.bootstrap.Component
19573  * Bootstrap MenuItem class
19574  * @cfg {Boolean} submenu (true | false) default false
19575  * @cfg {String} html text of the item
19576  * @cfg {String} href the link
19577  * @cfg {Boolean} disable (true | false) default false
19578  * @cfg {Boolean} preventDefault (true | false) default true
19579  * @cfg {String} icon Font awesome icon
19580  * @cfg {String} pos Submenu align to (left | right) default right 
19581  * 
19582  * 
19583  * @constructor
19584  * Create a new Item
19585  * @param {Object} config The config object
19586  */
19587
19588
19589 Roo.bootstrap.menu.Item = function(config){
19590     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
19591     this.addEvents({
19592         /**
19593          * @event mouseover
19594          * Fires when the mouse is hovering over this menu
19595          * @param {Roo.bootstrap.menu.Item} this
19596          * @param {Roo.EventObject} e
19597          */
19598         mouseover : true,
19599         /**
19600          * @event mouseout
19601          * Fires when the mouse exits this menu
19602          * @param {Roo.bootstrap.menu.Item} this
19603          * @param {Roo.EventObject} e
19604          */
19605         mouseout : true,
19606         // raw events
19607         /**
19608          * @event click
19609          * The raw click event for the entire grid.
19610          * @param {Roo.EventObject} e
19611          */
19612         click : true
19613     });
19614 };
19615
19616 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
19617     
19618     submenu : false,
19619     href : '',
19620     html : '',
19621     preventDefault: true,
19622     disable : false,
19623     icon : false,
19624     pos : 'right',
19625     
19626     getAutoCreate : function()
19627     {
19628         var text = [
19629             {
19630                 tag : 'span',
19631                 cls : 'roo-menu-item-text',
19632                 html : this.html
19633             }
19634         ];
19635         
19636         if(this.icon){
19637             text.unshift({
19638                 tag : 'i',
19639                 cls : 'fa ' + this.icon
19640             })
19641         }
19642         
19643         var cfg = {
19644             tag : 'li',
19645             cn : [
19646                 {
19647                     tag : 'a',
19648                     href : this.href || '#',
19649                     cn : text
19650                 }
19651             ]
19652         };
19653         
19654         if(this.disable){
19655             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
19656         }
19657         
19658         if(this.submenu){
19659             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
19660             
19661             if(this.pos == 'left'){
19662                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
19663             }
19664         }
19665         
19666         return cfg;
19667     },
19668     
19669     initEvents : function() 
19670     {
19671         this.el.on('mouseover', this.onMouseOver, this);
19672         this.el.on('mouseout', this.onMouseOut, this);
19673         
19674         this.el.select('a', true).first().on('click', this.onClick, this);
19675         
19676     },
19677     
19678     onClick : function(e)
19679     {
19680         if(this.preventDefault){
19681             e.preventDefault();
19682         }
19683         
19684         this.fireEvent("click", this, e);
19685     },
19686     
19687     onMouseOver : function(e)
19688     {
19689         if(this.submenu && this.pos == 'left'){
19690             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
19691         }
19692         
19693         this.fireEvent("mouseover", this, e);
19694     },
19695     
19696     onMouseOut : function(e)
19697     {
19698         this.fireEvent("mouseout", this, e);
19699     }
19700 });
19701
19702  
19703
19704  /*
19705  * - LGPL
19706  *
19707  * menu separator
19708  * 
19709  */
19710 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19711
19712 /**
19713  * @class Roo.bootstrap.menu.Separator
19714  * @extends Roo.bootstrap.Component
19715  * Bootstrap Separator class
19716  * 
19717  * @constructor
19718  * Create a new Separator
19719  * @param {Object} config The config object
19720  */
19721
19722
19723 Roo.bootstrap.menu.Separator = function(config){
19724     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
19725 };
19726
19727 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
19728     
19729     getAutoCreate : function(){
19730         var cfg = {
19731             tag : 'li',
19732             cls: 'divider'
19733         };
19734         
19735         return cfg;
19736     }
19737    
19738 });
19739
19740  
19741
19742