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 });
962
963  /*
964  * - LGPL
965  *
966  * image
967  * 
968  */
969
970
971 /**
972  * @class Roo.bootstrap.Img
973  * @extends Roo.bootstrap.Component
974  * Bootstrap Img class
975  * @cfg {Boolean} imgResponsive false | true
976  * @cfg {String} border rounded | circle | thumbnail
977  * @cfg {String} src image source
978  * @cfg {String} alt image alternative text
979  * @cfg {String} href a tag href
980  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
981  * 
982  * @constructor
983  * Create a new Input
984  * @param {Object} config The config object
985  */
986
987 Roo.bootstrap.Img = function(config){
988     Roo.bootstrap.Img.superclass.constructor.call(this, config);
989     
990     this.addEvents({
991         // img events
992         /**
993          * @event click
994          * The img click event for the img.
995          * @param {Roo.EventObject} e
996          */
997         "click" : true
998     });
999 };
1000
1001 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1002     
1003     imgResponsive: true,
1004     border: '',
1005     src: '',
1006     href: false,
1007     target: false,
1008
1009     getAutoCreate : function(){
1010         
1011         var cfg = {
1012             tag: 'img',
1013             cls: (this.imgResponsive) ? 'img-responsive' : '',
1014             html : null
1015         }
1016         
1017         cfg.html = this.html || cfg.html;
1018         
1019         cfg.src = this.src || cfg.src;
1020         
1021         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1022             cfg.cls += ' img-' + this.border;
1023         }
1024         
1025         if(this.alt){
1026             cfg.alt = this.alt;
1027         }
1028         
1029         if(this.href){
1030             var a = {
1031                 tag: 'a',
1032                 href: this.href,
1033                 cn: [
1034                     cfg
1035                 ]
1036             }
1037             
1038             if(this.target){
1039                 a.target = this.target;
1040             }
1041             
1042         }
1043         
1044         
1045         return (this.href) ? a : cfg;
1046     },
1047     
1048     initEvents: function() {
1049         
1050         if(!this.href){
1051             this.el.on('click', this.onClick, this);
1052         }
1053     },
1054     
1055     onClick : function(e)
1056     {
1057         Roo.log('img onclick');
1058         this.fireEvent('click', this, e);
1059     }
1060    
1061 });
1062
1063  /*
1064  * - LGPL
1065  *
1066  * image
1067  * 
1068  */
1069
1070
1071 /**
1072  * @class Roo.bootstrap.Link
1073  * @extends Roo.bootstrap.Component
1074  * Bootstrap Link Class
1075  * @cfg {String} alt image alternative text
1076  * @cfg {String} href a tag href
1077  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1078  * @cfg {String} html the content of the link.
1079
1080  * 
1081  * @constructor
1082  * Create a new Input
1083  * @param {Object} config The config object
1084  */
1085
1086 Roo.bootstrap.Link = function(config){
1087     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1088     
1089     this.addEvents({
1090         // img events
1091         /**
1092          * @event click
1093          * The img click event for the img.
1094          * @param {Roo.EventObject} e
1095          */
1096         "click" : true
1097     });
1098 };
1099
1100 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1101     
1102     href: false,
1103     target: false,
1104
1105     getAutoCreate : function(){
1106         
1107         var cfg = {
1108             tag: 'a',
1109             html : this.html || 'html-missing'
1110         }
1111         
1112         
1113         if(this.alt){
1114             cfg.alt = this.alt;
1115         }
1116         cfg.href = this.href || '#';
1117         if(this.target){
1118             cfg.target = this.target;
1119         }
1120         
1121         return cfg;
1122     },
1123     
1124     initEvents: function() {
1125         
1126         if(!this.href){
1127             this.el.on('click', this.onClick, this);
1128         }
1129     },
1130     
1131     onClick : function(e)
1132     {
1133         //Roo.log('img onclick');
1134         this.fireEvent('click', this, e);
1135     }
1136    
1137 });
1138
1139  /*
1140  * - LGPL
1141  *
1142  * header
1143  * 
1144  */
1145
1146 /**
1147  * @class Roo.bootstrap.Header
1148  * @extends Roo.bootstrap.Component
1149  * Bootstrap Header class
1150  * @cfg {String} html content of header
1151  * @cfg {Number} level (1|2|3|4|5|6) default 1
1152  * 
1153  * @constructor
1154  * Create a new Header
1155  * @param {Object} config The config object
1156  */
1157
1158
1159 Roo.bootstrap.Header  = function(config){
1160     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1161 };
1162
1163 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1164     
1165     //href : false,
1166     html : false,
1167     level : 1,
1168     
1169     
1170     
1171     getAutoCreate : function(){
1172         
1173         var cfg = {
1174             tag: 'h' + (1 *this.level),
1175             html: this.html || 'fill in html'
1176         } ;
1177         
1178         return cfg;
1179     }
1180    
1181 });
1182
1183  
1184
1185  /*
1186  * Based on:
1187  * Ext JS Library 1.1.1
1188  * Copyright(c) 2006-2007, Ext JS, LLC.
1189  *
1190  * Originally Released Under LGPL - original licence link has changed is not relivant.
1191  *
1192  * Fork - LGPL
1193  * <script type="text/javascript">
1194  */
1195  
1196 /**
1197  * @class Roo.bootstrap.MenuMgr
1198  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1199  * @singleton
1200  */
1201 Roo.bootstrap.MenuMgr = function(){
1202    var menus, active, groups = {}, attached = false, lastShow = new Date();
1203
1204    // private - called when first menu is created
1205    function init(){
1206        menus = {};
1207        active = new Roo.util.MixedCollection();
1208        Roo.get(document).addKeyListener(27, function(){
1209            if(active.length > 0){
1210                hideAll();
1211            }
1212        });
1213    }
1214
1215    // private
1216    function hideAll(){
1217        if(active && active.length > 0){
1218            var c = active.clone();
1219            c.each(function(m){
1220                m.hide();
1221            });
1222        }
1223    }
1224
1225    // private
1226    function onHide(m){
1227        active.remove(m);
1228        if(active.length < 1){
1229            Roo.get(document).un("mouseup", onMouseDown);
1230             
1231            attached = false;
1232        }
1233    }
1234
1235    // private
1236    function onShow(m){
1237        var last = active.last();
1238        lastShow = new Date();
1239        active.add(m);
1240        if(!attached){
1241           Roo.get(document).on("mouseup", onMouseDown);
1242            
1243            attached = true;
1244        }
1245        if(m.parentMenu){
1246           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1247           m.parentMenu.activeChild = m;
1248        }else if(last && last.isVisible()){
1249           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1250        }
1251    }
1252
1253    // private
1254    function onBeforeHide(m){
1255        if(m.activeChild){
1256            m.activeChild.hide();
1257        }
1258        if(m.autoHideTimer){
1259            clearTimeout(m.autoHideTimer);
1260            delete m.autoHideTimer;
1261        }
1262    }
1263
1264    // private
1265    function onBeforeShow(m){
1266        var pm = m.parentMenu;
1267        if(!pm && !m.allowOtherMenus){
1268            hideAll();
1269        }else if(pm && pm.activeChild && active != m){
1270            pm.activeChild.hide();
1271        }
1272    }
1273
1274    // private
1275    function onMouseDown(e){
1276         Roo.log("on MouseDown");
1277         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1278            hideAll();
1279         }
1280         
1281         
1282    }
1283
1284    // private
1285    function onBeforeCheck(mi, state){
1286        if(state){
1287            var g = groups[mi.group];
1288            for(var i = 0, l = g.length; i < l; i++){
1289                if(g[i] != mi){
1290                    g[i].setChecked(false);
1291                }
1292            }
1293        }
1294    }
1295
1296    return {
1297
1298        /**
1299         * Hides all menus that are currently visible
1300         */
1301        hideAll : function(){
1302             hideAll();  
1303        },
1304
1305        // private
1306        register : function(menu){
1307            if(!menus){
1308                init();
1309            }
1310            menus[menu.id] = menu;
1311            menu.on("beforehide", onBeforeHide);
1312            menu.on("hide", onHide);
1313            menu.on("beforeshow", onBeforeShow);
1314            menu.on("show", onShow);
1315            var g = menu.group;
1316            if(g && menu.events["checkchange"]){
1317                if(!groups[g]){
1318                    groups[g] = [];
1319                }
1320                groups[g].push(menu);
1321                menu.on("checkchange", onCheck);
1322            }
1323        },
1324
1325         /**
1326          * Returns a {@link Roo.menu.Menu} object
1327          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1328          * be used to generate and return a new Menu instance.
1329          */
1330        get : function(menu){
1331            if(typeof menu == "string"){ // menu id
1332                return menus[menu];
1333            }else if(menu.events){  // menu instance
1334                return menu;
1335            }
1336            /*else if(typeof menu.length == 'number'){ // array of menu items?
1337                return new Roo.bootstrap.Menu({items:menu});
1338            }else{ // otherwise, must be a config
1339                return new Roo.bootstrap.Menu(menu);
1340            }
1341            */
1342            return false;
1343        },
1344
1345        // private
1346        unregister : function(menu){
1347            delete menus[menu.id];
1348            menu.un("beforehide", onBeforeHide);
1349            menu.un("hide", onHide);
1350            menu.un("beforeshow", onBeforeShow);
1351            menu.un("show", onShow);
1352            var g = menu.group;
1353            if(g && menu.events["checkchange"]){
1354                groups[g].remove(menu);
1355                menu.un("checkchange", onCheck);
1356            }
1357        },
1358
1359        // private
1360        registerCheckable : function(menuItem){
1361            var g = menuItem.group;
1362            if(g){
1363                if(!groups[g]){
1364                    groups[g] = [];
1365                }
1366                groups[g].push(menuItem);
1367                menuItem.on("beforecheckchange", onBeforeCheck);
1368            }
1369        },
1370
1371        // private
1372        unregisterCheckable : function(menuItem){
1373            var g = menuItem.group;
1374            if(g){
1375                groups[g].remove(menuItem);
1376                menuItem.un("beforecheckchange", onBeforeCheck);
1377            }
1378        }
1379    };
1380 }();/*
1381  * - LGPL
1382  *
1383  * menu
1384  * 
1385  */
1386
1387 /**
1388  * @class Roo.bootstrap.Menu
1389  * @extends Roo.bootstrap.Component
1390  * Bootstrap Menu class - container for MenuItems
1391  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1392  * 
1393  * @constructor
1394  * Create a new Menu
1395  * @param {Object} config The config object
1396  */
1397
1398
1399 Roo.bootstrap.Menu = function(config){
1400     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1401     if (this.registerMenu) {
1402         Roo.bootstrap.MenuMgr.register(this);
1403     }
1404     this.addEvents({
1405         /**
1406          * @event beforeshow
1407          * Fires before this menu is displayed
1408          * @param {Roo.menu.Menu} this
1409          */
1410         beforeshow : true,
1411         /**
1412          * @event beforehide
1413          * Fires before this menu is hidden
1414          * @param {Roo.menu.Menu} this
1415          */
1416         beforehide : true,
1417         /**
1418          * @event show
1419          * Fires after this menu is displayed
1420          * @param {Roo.menu.Menu} this
1421          */
1422         show : true,
1423         /**
1424          * @event hide
1425          * Fires after this menu is hidden
1426          * @param {Roo.menu.Menu} this
1427          */
1428         hide : true,
1429         /**
1430          * @event click
1431          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1432          * @param {Roo.menu.Menu} this
1433          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1434          * @param {Roo.EventObject} e
1435          */
1436         click : true,
1437         /**
1438          * @event mouseover
1439          * Fires when the mouse is hovering over this menu
1440          * @param {Roo.menu.Menu} this
1441          * @param {Roo.EventObject} e
1442          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1443          */
1444         mouseover : true,
1445         /**
1446          * @event mouseout
1447          * Fires when the mouse exits this menu
1448          * @param {Roo.menu.Menu} this
1449          * @param {Roo.EventObject} e
1450          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1451          */
1452         mouseout : true,
1453         /**
1454          * @event itemclick
1455          * Fires when a menu item contained in this menu is clicked
1456          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1457          * @param {Roo.EventObject} e
1458          */
1459         itemclick: true
1460     });
1461     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1462 };
1463
1464 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1465     
1466    /// html : false,
1467     //align : '',
1468     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1469     type: false,
1470     /**
1471      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1472      */
1473     registerMenu : true,
1474     
1475     menuItems :false, // stores the menu items..
1476     
1477     hidden:true,
1478     
1479     parentMenu : false,
1480     
1481     getChildContainer : function() {
1482         return this.el;  
1483     },
1484     
1485     getAutoCreate : function(){
1486          
1487         //if (['right'].indexOf(this.align)!==-1) {
1488         //    cfg.cn[1].cls += ' pull-right'
1489         //}
1490         
1491         
1492         var cfg = {
1493             tag : 'ul',
1494             cls : 'dropdown-menu' ,
1495             style : 'z-index:1000'
1496             
1497         }
1498         
1499         if (this.type === 'submenu') {
1500             cfg.cls = 'submenu active';
1501         }
1502         if (this.type === 'treeview') {
1503             cfg.cls = 'treeview-menu';
1504         }
1505         
1506         return cfg;
1507     },
1508     initEvents : function() {
1509         
1510        // Roo.log("ADD event");
1511        // Roo.log(this.triggerEl.dom);
1512         this.triggerEl.on('click', this.onTriggerPress, this);
1513         this.triggerEl.addClass('dropdown-toggle');
1514         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1515
1516         this.el.on("mouseover", this.onMouseOver, this);
1517         this.el.on("mouseout", this.onMouseOut, this);
1518         
1519         
1520     },
1521     findTargetItem : function(e){
1522         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1523         if(!t){
1524             return false;
1525         }
1526         //Roo.log(t);         Roo.log(t.id);
1527         if(t && t.id){
1528             //Roo.log(this.menuitems);
1529             return this.menuitems.get(t.id);
1530             
1531             //return this.items.get(t.menuItemId);
1532         }
1533         
1534         return false;
1535     },
1536     onClick : function(e){
1537         Roo.log("menu.onClick");
1538         var t = this.findTargetItem(e);
1539         if(!t){
1540             return;
1541         }
1542         Roo.log(e);
1543         /*
1544         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1545             if(t == this.activeItem && t.shouldDeactivate(e)){
1546                 this.activeItem.deactivate();
1547                 delete this.activeItem;
1548                 return;
1549             }
1550             if(t.canActivate){
1551                 this.setActiveItem(t, true);
1552             }
1553             return;
1554             
1555             
1556         }
1557         */
1558         Roo.log('pass click event');
1559         
1560         t.onClick(e);
1561         
1562         this.fireEvent("click", this, t, e);
1563         
1564         this.hide();
1565     },
1566      onMouseOver : function(e){
1567         var t  = this.findTargetItem(e);
1568         //Roo.log(t);
1569         //if(t){
1570         //    if(t.canActivate && !t.disabled){
1571         //        this.setActiveItem(t, true);
1572         //    }
1573         //}
1574         
1575         this.fireEvent("mouseover", this, e, t);
1576     },
1577     isVisible : function(){
1578         return !this.hidden;
1579     },
1580      onMouseOut : function(e){
1581         var t  = this.findTargetItem(e);
1582         
1583         //if(t ){
1584         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1585         //        this.activeItem.deactivate();
1586         //        delete this.activeItem;
1587         //    }
1588         //}
1589         this.fireEvent("mouseout", this, e, t);
1590     },
1591     
1592     
1593     /**
1594      * Displays this menu relative to another element
1595      * @param {String/HTMLElement/Roo.Element} element The element to align to
1596      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1597      * the element (defaults to this.defaultAlign)
1598      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1599      */
1600     show : function(el, pos, parentMenu){
1601         this.parentMenu = parentMenu;
1602         if(!this.el){
1603             this.render();
1604         }
1605         this.fireEvent("beforeshow", this);
1606         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1607     },
1608      /**
1609      * Displays this menu at a specific xy position
1610      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1611      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1612      */
1613     showAt : function(xy, parentMenu, /* private: */_e){
1614         this.parentMenu = parentMenu;
1615         if(!this.el){
1616             this.render();
1617         }
1618         if(_e !== false){
1619             this.fireEvent("beforeshow", this);
1620             
1621             //xy = this.el.adjustForConstraints(xy);
1622         }
1623         //this.el.setXY(xy);
1624         //this.el.show();
1625         this.hideMenuItems();
1626         this.hidden = false;
1627         this.triggerEl.addClass('open');
1628         this.focus();
1629         this.fireEvent("show", this);
1630     },
1631     
1632     focus : function(){
1633         return;
1634         if(!this.hidden){
1635             this.doFocus.defer(50, this);
1636         }
1637     },
1638
1639     doFocus : function(){
1640         if(!this.hidden){
1641             this.focusEl.focus();
1642         }
1643     },
1644
1645     /**
1646      * Hides this menu and optionally all parent menus
1647      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1648      */
1649     hide : function(deep){
1650         
1651         this.hideMenuItems();
1652         if(this.el && this.isVisible()){
1653             this.fireEvent("beforehide", this);
1654             if(this.activeItem){
1655                 this.activeItem.deactivate();
1656                 this.activeItem = null;
1657             }
1658             this.triggerEl.removeClass('open');;
1659             this.hidden = true;
1660             this.fireEvent("hide", this);
1661         }
1662         if(deep === true && this.parentMenu){
1663             this.parentMenu.hide(true);
1664         }
1665     },
1666     
1667     onTriggerPress  : function(e)
1668     {
1669         
1670         Roo.log('trigger press');
1671         //Roo.log(e.getTarget());
1672        // Roo.log(this.triggerEl.dom);
1673         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1674             return;
1675         }
1676         if (this.isVisible()) {
1677             Roo.log('hide');
1678             this.hide();
1679         } else {
1680             this.show(this.triggerEl, false, false);
1681         }
1682         
1683         
1684     },
1685     
1686          
1687        
1688     
1689     hideMenuItems : function()
1690     {
1691         //$(backdrop).remove()
1692         Roo.select('.open',true).each(function(aa) {
1693             
1694             aa.removeClass('open');
1695           //var parent = getParent($(this))
1696           //var relatedTarget = { relatedTarget: this }
1697           
1698            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1699           //if (e.isDefaultPrevented()) return
1700            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1701         })
1702     },
1703     addxtypeChild : function (tree, cntr) {
1704         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1705           
1706         this.menuitems.add(comp);
1707         return comp;
1708
1709     },
1710     getEl : function()
1711     {
1712         Roo.log(this.el);
1713         return this.el;
1714     }
1715 });
1716
1717  
1718  /*
1719  * - LGPL
1720  *
1721  * menu item
1722  * 
1723  */
1724
1725
1726 /**
1727  * @class Roo.bootstrap.MenuItem
1728  * @extends Roo.bootstrap.Component
1729  * Bootstrap MenuItem class
1730  * @cfg {String} html the menu label
1731  * @cfg {String} href the link
1732  * @cfg {Boolean} preventDefault (true | false) default true
1733  * 
1734  * 
1735  * @constructor
1736  * Create a new MenuItem
1737  * @param {Object} config The config object
1738  */
1739
1740
1741 Roo.bootstrap.MenuItem = function(config){
1742     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1743     this.addEvents({
1744         // raw events
1745         /**
1746          * @event click
1747          * The raw click event for the entire grid.
1748          * @param {Roo.EventObject} e
1749          */
1750         "click" : true
1751     });
1752 };
1753
1754 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1755     
1756     href : false,
1757     html : false,
1758     preventDefault: true,
1759     
1760     getAutoCreate : function(){
1761         var cfg= {
1762             tag: 'li',
1763             cls: 'dropdown-menu-item',
1764             cn: [
1765                     {
1766                         tag : 'a',
1767                         href : '#',
1768                         html : 'Link'
1769                     }
1770                 ]
1771         };
1772         if (this.parent().type == 'treeview') {
1773             cfg.cls = 'treeview-menu';
1774         }
1775         
1776         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1777         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1778         return cfg;
1779     },
1780     
1781     initEvents: function() {
1782         
1783         //this.el.select('a').on('click', this.onClick, this);
1784         
1785     },
1786     onClick : function(e)
1787     {
1788         Roo.log('item on click ');
1789         //if(this.preventDefault){
1790         //    e.preventDefault();
1791         //}
1792         //this.parent().hideMenuItems();
1793         
1794         this.fireEvent('click', this, e);
1795     },
1796     getEl : function()
1797     {
1798         return this.el;
1799     }
1800 });
1801
1802  
1803
1804  /*
1805  * - LGPL
1806  *
1807  * menu separator
1808  * 
1809  */
1810
1811
1812 /**
1813  * @class Roo.bootstrap.MenuSeparator
1814  * @extends Roo.bootstrap.Component
1815  * Bootstrap MenuSeparator class
1816  * 
1817  * @constructor
1818  * Create a new MenuItem
1819  * @param {Object} config The config object
1820  */
1821
1822
1823 Roo.bootstrap.MenuSeparator = function(config){
1824     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1825 };
1826
1827 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1828     
1829     getAutoCreate : function(){
1830         var cfg = {
1831             cls: 'divider',
1832             tag : 'li'
1833         };
1834         
1835         return cfg;
1836     }
1837    
1838 });
1839
1840  
1841
1842  
1843 /*
1844 <div class="modal fade">
1845   <div class="modal-dialog">
1846     <div class="modal-content">
1847       <div class="modal-header">
1848         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1849         <h4 class="modal-title">Modal title</h4>
1850       </div>
1851       <div class="modal-body">
1852         <p>One fine body&hellip;</p>
1853       </div>
1854       <div class="modal-footer">
1855         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1856         <button type="button" class="btn btn-primary">Save changes</button>
1857       </div>
1858     </div><!-- /.modal-content -->
1859   </div><!-- /.modal-dialog -->
1860 </div><!-- /.modal -->
1861 */
1862 /*
1863  * - LGPL
1864  *
1865  * page contgainer.
1866  * 
1867  */
1868
1869 /**
1870  * @class Roo.bootstrap.Modal
1871  * @extends Roo.bootstrap.Component
1872  * Bootstrap Modal class
1873  * @cfg {String} title Title of dialog
1874  * @cfg {Boolean} specificTitle (true|false) default false
1875  * @cfg {Array} buttons Array of buttons or standard button set..
1876  * @cfg {String} buttonPosition (left|right|center) default right
1877  * 
1878  * @constructor
1879  * Create a new Modal Dialog
1880  * @param {Object} config The config object
1881  */
1882
1883 Roo.bootstrap.Modal = function(config){
1884     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1885     this.addEvents({
1886         // raw events
1887         /**
1888          * @event btnclick
1889          * The raw btnclick event for the button
1890          * @param {Roo.EventObject} e
1891          */
1892         "btnclick" : true
1893     });
1894     this.buttons = this.buttons || [];
1895 };
1896
1897 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1898     
1899     title : 'test dialog',
1900    
1901     buttons : false,
1902     
1903     // set on load...
1904     body:  false,
1905     
1906     specificTitle: false,
1907     
1908     buttonPosition: 'right',
1909     
1910     onRender : function(ct, position)
1911     {
1912         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1913      
1914         if(!this.el){
1915             var cfg = Roo.apply({},  this.getAutoCreate());
1916             cfg.id = Roo.id();
1917             //if(!cfg.name){
1918             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1919             //}
1920             //if (!cfg.name.length) {
1921             //    delete cfg.name;
1922            // }
1923             if (this.cls) {
1924                 cfg.cls += ' ' + this.cls;
1925             }
1926             if (this.style) {
1927                 cfg.style = this.style;
1928             }
1929             this.el = Roo.get(document.body).createChild(cfg, position);
1930         }
1931         //var type = this.el.dom.type;
1932         
1933         if(this.tabIndex !== undefined){
1934             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1935         }
1936         
1937         
1938         
1939         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1940         this.maskEl.enableDisplayMode("block");
1941         this.maskEl.hide();
1942         //this.el.addClass("x-dlg-modal");
1943     
1944         if (this.buttons.length) {
1945             Roo.each(this.buttons, function(bb) {
1946                 b = Roo.apply({}, bb);
1947                 b.xns = b.xns || Roo.bootstrap;
1948                 b.xtype = b.xtype || 'Button';
1949                 if (typeof(b.listeners) == 'undefined') {
1950                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1951                 }
1952                 
1953                 var btn = Roo.factory(b);
1954                 
1955                 btn.onRender(this.el.select('.modal-footer div').first());
1956                 
1957             },this);
1958         }
1959         // render the children.
1960         var nitems = [];
1961         
1962         if(typeof(this.items) != 'undefined'){
1963             var items = this.items;
1964             delete this.items;
1965
1966             for(var i =0;i < items.length;i++) {
1967                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1968             }
1969         }
1970         
1971         this.items = nitems;
1972         
1973         this.body = this.el.select('.modal-body',true).first();
1974         this.close = this.el.select('.modal-header .close', true).first();
1975         this.footer = this.el.select('.modal-footer',true).first();
1976         this.initEvents();
1977         //this.el.addClass([this.fieldClass, this.cls]);
1978         
1979     },
1980     getAutoCreate : function(){
1981         
1982         
1983         var bdy = {
1984                 cls : 'modal-body',
1985                 html : this.html || ''
1986         };
1987         
1988         var title = {
1989             tag: 'h4',
1990             cls : 'modal-title',
1991             html : this.title
1992         };
1993         
1994         if(this.specificTitle){
1995             title = this.title;
1996         };
1997         
1998         return modal = {
1999             cls: "modal fade",
2000             style : 'display: none',
2001             cn : [
2002                 {
2003                     cls: "modal-dialog",
2004                     cn : [
2005                         {
2006                             cls : "modal-content",
2007                             cn : [
2008                                 {
2009                                     cls : 'modal-header',
2010                                     cn : [
2011                                         {
2012                                             tag: 'button',
2013                                             cls : 'close',
2014                                             html : '&times'
2015                                         },
2016                                         title
2017                                     ]
2018                                 },
2019                                 bdy,
2020                                 {
2021                                     cls : 'modal-footer',
2022                                     cn : [
2023                                         {
2024                                             tag: 'div',
2025                                             cls: 'btn-' + this.buttonPosition
2026                                         }
2027                                     ]
2028                                     
2029                                 }
2030                                 
2031                                 
2032                             ]
2033                             
2034                         }
2035                     ]
2036                         
2037                 }
2038             ]
2039             
2040             
2041         };
2042           
2043     },
2044     getChildContainer : function() {
2045          
2046          return this.el.select('.modal-body',true).first();
2047         
2048     },
2049     getButtonContainer : function() {
2050          return this.el.select('.modal-footer div',true).first();
2051         
2052     },
2053     initEvents : function()
2054     {
2055         this.el.select('.modal-header .close').on('click', this.hide, this);
2056 //        
2057 //        this.addxtype(this);
2058     },
2059     show : function() {
2060         
2061         if (!this.rendered) {
2062             this.render();
2063         }
2064        
2065         this.el.addClass('on');
2066         this.el.removeClass('fade');
2067         this.el.setStyle('display', 'block');
2068         Roo.get(document.body).addClass("x-body-masked");
2069         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2070         this.maskEl.show();
2071         this.el.setStyle('zIndex', '10001');
2072         this.fireEvent('show', this);
2073         
2074         
2075     },
2076     hide : function()
2077     {
2078         Roo.log('Modal hide?!');
2079         this.maskEl.hide();
2080         Roo.get(document.body).removeClass("x-body-masked");
2081         this.el.removeClass('on');
2082         this.el.addClass('fade');
2083         this.el.setStyle('display', 'none');
2084         this.fireEvent('hide', this);
2085     },
2086     
2087     addButton : function(str, cb)
2088     {
2089          
2090         
2091         var b = Roo.apply({}, { html : str } );
2092         b.xns = b.xns || Roo.bootstrap;
2093         b.xtype = b.xtype || 'Button';
2094         if (typeof(b.listeners) == 'undefined') {
2095             b.listeners = { click : cb.createDelegate(this)  };
2096         }
2097         
2098         var btn = Roo.factory(b);
2099            
2100         btn.onRender(this.el.select('.modal-footer div').first());
2101         
2102         return btn;   
2103        
2104     },
2105     
2106     setDefaultButton : function(btn)
2107     {
2108         //this.el.select('.modal-footer').()
2109     },
2110     resizeTo: function(w,h)
2111     {
2112         // skip..
2113     },
2114     setContentSize  : function(w, h)
2115     {
2116         
2117     },
2118     onButtonClick: function(btn,e)
2119     {
2120         //Roo.log([a,b,c]);
2121         this.fireEvent('btnclick', btn.name, e);
2122     },
2123     setTitle: function(str) {
2124         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2125         
2126     }
2127 });
2128
2129
2130 Roo.apply(Roo.bootstrap.Modal,  {
2131     /**
2132          * Button config that displays a single OK button
2133          * @type Object
2134          */
2135         OK :  [{
2136             name : 'ok',
2137             weight : 'primary',
2138             html : 'OK'
2139         }], 
2140         /**
2141          * Button config that displays Yes and No buttons
2142          * @type Object
2143          */
2144         YESNO : [
2145             {
2146                 name  : 'no',
2147                 html : 'No'
2148             },
2149             {
2150                 name  :'yes',
2151                 weight : 'primary',
2152                 html : 'Yes'
2153             }
2154         ],
2155         
2156         /**
2157          * Button config that displays OK and Cancel buttons
2158          * @type Object
2159          */
2160         OKCANCEL : [
2161             {
2162                name : 'cancel',
2163                 html : 'Cancel'
2164             },
2165             {
2166                 name : 'ok',
2167                 weight : 'primary',
2168                 html : 'OK'
2169             }
2170         ],
2171         /**
2172          * Button config that displays Yes, No and Cancel buttons
2173          * @type Object
2174          */
2175         YESNOCANCEL : [
2176             {
2177                 name : 'yes',
2178                 weight : 'primary',
2179                 html : 'Yes'
2180             },
2181             {
2182                 name : 'no',
2183                 html : 'No'
2184             },
2185             {
2186                 name : 'cancel',
2187                 html : 'Cancel'
2188             }
2189         ]
2190 });
2191  /*
2192  * - LGPL
2193  *
2194  * messagebox - can be used as a replace
2195  * 
2196  */
2197 /**
2198  * @class Roo.MessageBox
2199  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2200  * Example usage:
2201  *<pre><code>
2202 // Basic alert:
2203 Roo.Msg.alert('Status', 'Changes saved successfully.');
2204
2205 // Prompt for user data:
2206 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2207     if (btn == 'ok'){
2208         // process text value...
2209     }
2210 });
2211
2212 // Show a dialog using config options:
2213 Roo.Msg.show({
2214    title:'Save Changes?',
2215    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2216    buttons: Roo.Msg.YESNOCANCEL,
2217    fn: processResult,
2218    animEl: 'elId'
2219 });
2220 </code></pre>
2221  * @singleton
2222  */
2223 Roo.bootstrap.MessageBox = function(){
2224     var dlg, opt, mask, waitTimer;
2225     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2226     var buttons, activeTextEl, bwidth;
2227
2228     
2229     // private
2230     var handleButton = function(button){
2231         dlg.hide();
2232         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2233     };
2234
2235     // private
2236     var handleHide = function(){
2237         if(opt && opt.cls){
2238             dlg.el.removeClass(opt.cls);
2239         }
2240         //if(waitTimer){
2241         //    Roo.TaskMgr.stop(waitTimer);
2242         //    waitTimer = null;
2243         //}
2244     };
2245
2246     // private
2247     var updateButtons = function(b){
2248         var width = 0;
2249         if(!b){
2250             buttons["ok"].hide();
2251             buttons["cancel"].hide();
2252             buttons["yes"].hide();
2253             buttons["no"].hide();
2254             //dlg.footer.dom.style.display = 'none';
2255             return width;
2256         }
2257         dlg.footer.dom.style.display = '';
2258         for(var k in buttons){
2259             if(typeof buttons[k] != "function"){
2260                 if(b[k]){
2261                     buttons[k].show();
2262                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2263                     width += buttons[k].el.getWidth()+15;
2264                 }else{
2265                     buttons[k].hide();
2266                 }
2267             }
2268         }
2269         return width;
2270     };
2271
2272     // private
2273     var handleEsc = function(d, k, e){
2274         if(opt && opt.closable !== false){
2275             dlg.hide();
2276         }
2277         if(e){
2278             e.stopEvent();
2279         }
2280     };
2281
2282     return {
2283         /**
2284          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2285          * @return {Roo.BasicDialog} The BasicDialog element
2286          */
2287         getDialog : function(){
2288            if(!dlg){
2289                 dlg = new Roo.bootstrap.Modal( {
2290                     //draggable: true,
2291                     //resizable:false,
2292                     //constraintoviewport:false,
2293                     //fixedcenter:true,
2294                     //collapsible : false,
2295                     //shim:true,
2296                     //modal: true,
2297                   //  width:400,
2298                   //  height:100,
2299                     //buttonAlign:"center",
2300                     closeClick : function(){
2301                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2302                             handleButton("no");
2303                         }else{
2304                             handleButton("cancel");
2305                         }
2306                     }
2307                 });
2308                 dlg.render();
2309                 dlg.on("hide", handleHide);
2310                 mask = dlg.mask;
2311                 //dlg.addKeyListener(27, handleEsc);
2312                 buttons = {};
2313                 this.buttons = buttons;
2314                 var bt = this.buttonText;
2315                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2316                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2317                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2318                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2319                 Roo.log(buttons)
2320                 bodyEl = dlg.body.createChild({
2321
2322                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2323                         '<textarea class="roo-mb-textarea"></textarea>' +
2324                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2325                 });
2326                 msgEl = bodyEl.dom.firstChild;
2327                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2328                 textboxEl.enableDisplayMode();
2329                 textboxEl.addKeyListener([10,13], function(){
2330                     if(dlg.isVisible() && opt && opt.buttons){
2331                         if(opt.buttons.ok){
2332                             handleButton("ok");
2333                         }else if(opt.buttons.yes){
2334                             handleButton("yes");
2335                         }
2336                     }
2337                 });
2338                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2339                 textareaEl.enableDisplayMode();
2340                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2341                 progressEl.enableDisplayMode();
2342                 var pf = progressEl.dom.firstChild;
2343                 if (pf) {
2344                     pp = Roo.get(pf.firstChild);
2345                     pp.setHeight(pf.offsetHeight);
2346                 }
2347                 
2348             }
2349             return dlg;
2350         },
2351
2352         /**
2353          * Updates the message box body text
2354          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2355          * the XHTML-compliant non-breaking space character '&amp;#160;')
2356          * @return {Roo.MessageBox} This message box
2357          */
2358         updateText : function(text){
2359             if(!dlg.isVisible() && !opt.width){
2360                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2361             }
2362             msgEl.innerHTML = text || '&#160;';
2363       
2364             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2365             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2366             var w = Math.max(
2367                     Math.min(opt.width || cw , this.maxWidth), 
2368                     Math.max(opt.minWidth || this.minWidth, bwidth)
2369             );
2370             if(opt.prompt){
2371                 activeTextEl.setWidth(w);
2372             }
2373             if(dlg.isVisible()){
2374                 dlg.fixedcenter = false;
2375             }
2376             // to big, make it scroll. = But as usual stupid IE does not support
2377             // !important..
2378             
2379             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2380                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2381                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2382             } else {
2383                 bodyEl.dom.style.height = '';
2384                 bodyEl.dom.style.overflowY = '';
2385             }
2386             if (cw > w) {
2387                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2388             } else {
2389                 bodyEl.dom.style.overflowX = '';
2390             }
2391             
2392             dlg.setContentSize(w, bodyEl.getHeight());
2393             if(dlg.isVisible()){
2394                 dlg.fixedcenter = true;
2395             }
2396             return this;
2397         },
2398
2399         /**
2400          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2401          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2402          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2403          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2404          * @return {Roo.MessageBox} This message box
2405          */
2406         updateProgress : function(value, text){
2407             if(text){
2408                 this.updateText(text);
2409             }
2410             if (pp) { // weird bug on my firefox - for some reason this is not defined
2411                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2412             }
2413             return this;
2414         },        
2415
2416         /**
2417          * Returns true if the message box is currently displayed
2418          * @return {Boolean} True if the message box is visible, else false
2419          */
2420         isVisible : function(){
2421             return dlg && dlg.isVisible();  
2422         },
2423
2424         /**
2425          * Hides the message box if it is displayed
2426          */
2427         hide : function(){
2428             if(this.isVisible()){
2429                 dlg.hide();
2430             }  
2431         },
2432
2433         /**
2434          * Displays a new message box, or reinitializes an existing message box, based on the config options
2435          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2436          * The following config object properties are supported:
2437          * <pre>
2438 Property    Type             Description
2439 ----------  ---------------  ------------------------------------------------------------------------------------
2440 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2441                                    closes (defaults to undefined)
2442 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2443                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2444 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2445                                    progress and wait dialogs will ignore this property and always hide the
2446                                    close button as they can only be closed programmatically.
2447 cls               String           A custom CSS class to apply to the message box element
2448 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2449                                    displayed (defaults to 75)
2450 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2451                                    function will be btn (the name of the button that was clicked, if applicable,
2452                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2453                                    Progress and wait dialogs will ignore this option since they do not respond to
2454                                    user actions and can only be closed programmatically, so any required function
2455                                    should be called by the same code after it closes the dialog.
2456 icon              String           A CSS class that provides a background image to be used as an icon for
2457                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2458 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2459 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2460 modal             Boolean          False to allow user interaction with the page while the message box is
2461                                    displayed (defaults to true)
2462 msg               String           A string that will replace the existing message box body text (defaults
2463                                    to the XHTML-compliant non-breaking space character '&#160;')
2464 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2465 progress          Boolean          True to display a progress bar (defaults to false)
2466 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2467 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2468 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2469 title             String           The title text
2470 value             String           The string value to set into the active textbox element if displayed
2471 wait              Boolean          True to display a progress bar (defaults to false)
2472 width             Number           The width of the dialog in pixels
2473 </pre>
2474          *
2475          * Example usage:
2476          * <pre><code>
2477 Roo.Msg.show({
2478    title: 'Address',
2479    msg: 'Please enter your address:',
2480    width: 300,
2481    buttons: Roo.MessageBox.OKCANCEL,
2482    multiline: true,
2483    fn: saveAddress,
2484    animEl: 'addAddressBtn'
2485 });
2486 </code></pre>
2487          * @param {Object} config Configuration options
2488          * @return {Roo.MessageBox} This message box
2489          */
2490         show : function(options)
2491         {
2492             
2493             // this causes nightmares if you show one dialog after another
2494             // especially on callbacks..
2495              
2496             if(this.isVisible()){
2497                 
2498                 this.hide();
2499                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2500                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2501                 Roo.log("New Dialog Message:" +  options.msg )
2502                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2503                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2504                 
2505             }
2506             var d = this.getDialog();
2507             opt = options;
2508             d.setTitle(opt.title || "&#160;");
2509             d.close.setDisplayed(opt.closable !== false);
2510             activeTextEl = textboxEl;
2511             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2512             if(opt.prompt){
2513                 if(opt.multiline){
2514                     textboxEl.hide();
2515                     textareaEl.show();
2516                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2517                         opt.multiline : this.defaultTextHeight);
2518                     activeTextEl = textareaEl;
2519                 }else{
2520                     textboxEl.show();
2521                     textareaEl.hide();
2522                 }
2523             }else{
2524                 textboxEl.hide();
2525                 textareaEl.hide();
2526             }
2527             progressEl.setDisplayed(opt.progress === true);
2528             this.updateProgress(0);
2529             activeTextEl.dom.value = opt.value || "";
2530             if(opt.prompt){
2531                 dlg.setDefaultButton(activeTextEl);
2532             }else{
2533                 var bs = opt.buttons;
2534                 var db = null;
2535                 if(bs && bs.ok){
2536                     db = buttons["ok"];
2537                 }else if(bs && bs.yes){
2538                     db = buttons["yes"];
2539                 }
2540                 dlg.setDefaultButton(db);
2541             }
2542             bwidth = updateButtons(opt.buttons);
2543             this.updateText(opt.msg);
2544             if(opt.cls){
2545                 d.el.addClass(opt.cls);
2546             }
2547             d.proxyDrag = opt.proxyDrag === true;
2548             d.modal = opt.modal !== false;
2549             d.mask = opt.modal !== false ? mask : false;
2550             if(!d.isVisible()){
2551                 // force it to the end of the z-index stack so it gets a cursor in FF
2552                 document.body.appendChild(dlg.el.dom);
2553                 d.animateTarget = null;
2554                 d.show(options.animEl);
2555             }
2556             return this;
2557         },
2558
2559         /**
2560          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2561          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2562          * and closing the message box when the process is complete.
2563          * @param {String} title The title bar text
2564          * @param {String} msg The message box body text
2565          * @return {Roo.MessageBox} This message box
2566          */
2567         progress : function(title, msg){
2568             this.show({
2569                 title : title,
2570                 msg : msg,
2571                 buttons: false,
2572                 progress:true,
2573                 closable:false,
2574                 minWidth: this.minProgressWidth,
2575                 modal : true
2576             });
2577             return this;
2578         },
2579
2580         /**
2581          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2582          * If a callback function is passed it will be called after the user clicks the button, and the
2583          * id of the button that was clicked will be passed as the only parameter to the callback
2584          * (could also be the top-right close button).
2585          * @param {String} title The title bar text
2586          * @param {String} msg The message box body text
2587          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2588          * @param {Object} scope (optional) The scope of the callback function
2589          * @return {Roo.MessageBox} This message box
2590          */
2591         alert : function(title, msg, fn, scope){
2592             this.show({
2593                 title : title,
2594                 msg : msg,
2595                 buttons: this.OK,
2596                 fn: fn,
2597                 scope : scope,
2598                 modal : true
2599             });
2600             return this;
2601         },
2602
2603         /**
2604          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2605          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2606          * You are responsible for closing the message box when the process is complete.
2607          * @param {String} msg The message box body text
2608          * @param {String} title (optional) The title bar text
2609          * @return {Roo.MessageBox} This message box
2610          */
2611         wait : function(msg, title){
2612             this.show({
2613                 title : title,
2614                 msg : msg,
2615                 buttons: false,
2616                 closable:false,
2617                 progress:true,
2618                 modal:true,
2619                 width:300,
2620                 wait:true
2621             });
2622             waitTimer = Roo.TaskMgr.start({
2623                 run: function(i){
2624                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2625                 },
2626                 interval: 1000
2627             });
2628             return this;
2629         },
2630
2631         /**
2632          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2633          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2634          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2635          * @param {String} title The title bar text
2636          * @param {String} msg The message box body text
2637          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2638          * @param {Object} scope (optional) The scope of the callback function
2639          * @return {Roo.MessageBox} This message box
2640          */
2641         confirm : function(title, msg, fn, scope){
2642             this.show({
2643                 title : title,
2644                 msg : msg,
2645                 buttons: this.YESNO,
2646                 fn: fn,
2647                 scope : scope,
2648                 modal : true
2649             });
2650             return this;
2651         },
2652
2653         /**
2654          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2655          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2656          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2657          * (could also be the top-right close button) and the text that was entered will be passed as the two
2658          * parameters to the callback.
2659          * @param {String} title The title bar text
2660          * @param {String} msg The message box body text
2661          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2662          * @param {Object} scope (optional) The scope of the callback function
2663          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2664          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2665          * @return {Roo.MessageBox} This message box
2666          */
2667         prompt : function(title, msg, fn, scope, multiline){
2668             this.show({
2669                 title : title,
2670                 msg : msg,
2671                 buttons: this.OKCANCEL,
2672                 fn: fn,
2673                 minWidth:250,
2674                 scope : scope,
2675                 prompt:true,
2676                 multiline: multiline,
2677                 modal : true
2678             });
2679             return this;
2680         },
2681
2682         /**
2683          * Button config that displays a single OK button
2684          * @type Object
2685          */
2686         OK : {ok:true},
2687         /**
2688          * Button config that displays Yes and No buttons
2689          * @type Object
2690          */
2691         YESNO : {yes:true, no:true},
2692         /**
2693          * Button config that displays OK and Cancel buttons
2694          * @type Object
2695          */
2696         OKCANCEL : {ok:true, cancel:true},
2697         /**
2698          * Button config that displays Yes, No and Cancel buttons
2699          * @type Object
2700          */
2701         YESNOCANCEL : {yes:true, no:true, cancel:true},
2702
2703         /**
2704          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2705          * @type Number
2706          */
2707         defaultTextHeight : 75,
2708         /**
2709          * The maximum width in pixels of the message box (defaults to 600)
2710          * @type Number
2711          */
2712         maxWidth : 600,
2713         /**
2714          * The minimum width in pixels of the message box (defaults to 100)
2715          * @type Number
2716          */
2717         minWidth : 100,
2718         /**
2719          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2720          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2721          * @type Number
2722          */
2723         minProgressWidth : 250,
2724         /**
2725          * An object containing the default button text strings that can be overriden for localized language support.
2726          * Supported properties are: ok, cancel, yes and no.
2727          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2728          * @type Object
2729          */
2730         buttonText : {
2731             ok : "OK",
2732             cancel : "Cancel",
2733             yes : "Yes",
2734             no : "No"
2735         }
2736     };
2737 }();
2738
2739 /**
2740  * Shorthand for {@link Roo.MessageBox}
2741  */
2742 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2743 Roo.Msg = Roo.Msg || Roo.MessageBox;
2744 /*
2745  * - LGPL
2746  *
2747  * navbar
2748  * 
2749  */
2750
2751 /**
2752  * @class Roo.bootstrap.Navbar
2753  * @extends Roo.bootstrap.Component
2754  * Bootstrap Navbar class
2755
2756  * @constructor
2757  * Create a new Navbar
2758  * @param {Object} config The config object
2759  */
2760
2761
2762 Roo.bootstrap.Navbar = function(config){
2763     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2764     
2765 };
2766
2767 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2768     
2769     
2770    
2771     // private
2772     navItems : false,
2773     loadMask : false,
2774     
2775     
2776     getAutoCreate : function(){
2777         
2778         
2779         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2780         
2781     },
2782     
2783     initEvents :function ()
2784     {
2785         //Roo.log(this.el.select('.navbar-toggle',true));
2786         this.el.select('.navbar-toggle',true).on('click', function() {
2787            // Roo.log('click');
2788             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2789         }, this);
2790         
2791         var mark = {
2792             tag: "div",
2793             cls:"x-dlg-mask"
2794         }
2795         
2796         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2797         
2798         var size = this.el.getSize();
2799         this.maskEl.setSize(size.width, size.height);
2800         this.maskEl.enableDisplayMode("block");
2801         this.maskEl.hide();
2802         
2803         if(this.loadMask){
2804             this.maskEl.show();
2805         }
2806     },
2807     
2808     
2809     getChildContainer : function()
2810     {
2811         if (this.el.select('.collapse').getCount()) {
2812             return this.el.select('.collapse',true).first();
2813         }
2814         
2815         return this.el;
2816     },
2817     
2818     mask : function()
2819     {
2820         this.maskEl.show();
2821     },
2822     
2823     unmask : function()
2824     {
2825         this.maskEl.hide();
2826     } 
2827     
2828     
2829     
2830     
2831 });
2832
2833
2834
2835  
2836
2837  /*
2838  * - LGPL
2839  *
2840  * navbar
2841  * 
2842  */
2843
2844 /**
2845  * @class Roo.bootstrap.NavSimplebar
2846  * @extends Roo.bootstrap.Navbar
2847  * Bootstrap Sidebar class
2848  *
2849  * @cfg {Boolean} inverse is inverted color
2850  * 
2851  * @cfg {String} type (nav | pills | tabs)
2852  * @cfg {Boolean} arrangement stacked | justified
2853  * @cfg {String} align (left | right) alignment
2854  * 
2855  * @cfg {Boolean} main (true|false) main nav bar? default false
2856  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2857  * 
2858  * @cfg {String} tag (header|footer|nav|div) default is nav 
2859
2860  * 
2861  * 
2862  * 
2863  * @constructor
2864  * Create a new Sidebar
2865  * @param {Object} config The config object
2866  */
2867
2868
2869 Roo.bootstrap.NavSimplebar = function(config){
2870     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2871 };
2872
2873 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2874     
2875     inverse: false,
2876     
2877     type: false,
2878     arrangement: '',
2879     align : false,
2880     
2881     
2882     
2883     main : false,
2884     
2885     
2886     tag : false,
2887     
2888     
2889     getAutoCreate : function(){
2890         
2891         
2892         var cfg = {
2893             tag : this.tag || 'div',
2894             cls : 'navbar'
2895         };
2896           
2897         
2898         cfg.cn = [
2899             {
2900                 cls: 'nav',
2901                 tag : 'ul'
2902             }
2903         ];
2904         
2905          
2906         this.type = this.type || 'nav';
2907         if (['tabs','pills'].indexOf(this.type)!==-1) {
2908             cfg.cn[0].cls += ' nav-' + this.type
2909         
2910         
2911         } else {
2912             if (this.type!=='nav') {
2913                 Roo.log('nav type must be nav/tabs/pills')
2914             }
2915             cfg.cn[0].cls += ' navbar-nav'
2916         }
2917         
2918         
2919         
2920         
2921         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2922             cfg.cn[0].cls += ' nav-' + this.arrangement;
2923         }
2924         
2925         
2926         if (this.align === 'right') {
2927             cfg.cn[0].cls += ' navbar-right';
2928         }
2929         
2930         if (this.inverse) {
2931             cfg.cls += ' navbar-inverse';
2932             
2933         }
2934         
2935         
2936         return cfg;
2937     
2938         
2939     }
2940     
2941     
2942     
2943 });
2944
2945
2946
2947  
2948
2949  
2950        /*
2951  * - LGPL
2952  *
2953  * navbar
2954  * 
2955  */
2956
2957 /**
2958  * @class Roo.bootstrap.NavHeaderbar
2959  * @extends Roo.bootstrap.NavSimplebar
2960  * Bootstrap Sidebar class
2961  *
2962  * @cfg {String} brand what is brand
2963  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2964  * @cfg {String} brand_href href of the brand
2965  * 
2966  * @constructor
2967  * Create a new Sidebar
2968  * @param {Object} config The config object
2969  */
2970
2971
2972 Roo.bootstrap.NavHeaderbar = function(config){
2973     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
2974 };
2975
2976 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
2977     
2978     position: '',
2979     brand: '',
2980     brand_href: false,
2981     
2982     
2983     getAutoCreate : function(){
2984         
2985         
2986         
2987         var   cfg = {
2988             tag: this.nav || 'nav',
2989             cls: 'navbar',
2990             role: 'navigation',
2991             cn: [
2992                 {
2993                     tag: 'div',
2994                     cls: 'navbar-header',
2995                     cn: [
2996                         {
2997                         tag: 'button',
2998                         type: 'button',
2999                         cls: 'navbar-toggle',
3000                         'data-toggle': 'collapse',
3001                         cn: [
3002                             {
3003                                 tag: 'span',
3004                                 cls: 'sr-only',
3005                                 html: 'Toggle navigation'
3006                             },
3007                             {
3008                                 tag: 'span',
3009                                 cls: 'icon-bar'
3010                             },
3011                             {
3012                                 tag: 'span',
3013                                 cls: 'icon-bar'
3014                             },
3015                             {
3016                                 tag: 'span',
3017                                 cls: 'icon-bar'
3018                             }
3019                         ]
3020                         }
3021                     ]
3022                 },
3023                 {
3024                 tag: 'div',
3025                 cls: 'collapse navbar-collapse'
3026                 }
3027             ]
3028         };
3029         
3030         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3031         
3032         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3033             cfg.cls += ' navbar-' + this.position;
3034             
3035             // tag can override this..
3036             
3037             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3038         }
3039         
3040         if (this.brand !== '') {
3041             cfg.cn[0].cn.push({
3042                 tag: 'a',
3043                 href: this.brand_href ? this.brand_href : '#',
3044                 cls: 'navbar-brand',
3045                 cn: [
3046                 this.brand
3047                 ]
3048             });
3049         }
3050         
3051         if(this.main){
3052             cfg.cls += ' main-nav';
3053         }
3054         
3055         
3056         return cfg;
3057
3058         
3059     }
3060     
3061     
3062     
3063 });
3064
3065
3066
3067  
3068
3069  /*
3070  * - LGPL
3071  *
3072  * navbar
3073  * 
3074  */
3075
3076 /**
3077  * @class Roo.bootstrap.NavSidebar
3078  * @extends Roo.bootstrap.Navbar
3079  * Bootstrap Sidebar class
3080  * 
3081  * @constructor
3082  * Create a new Sidebar
3083  * @param {Object} config The config object
3084  */
3085
3086
3087 Roo.bootstrap.NavSidebar = function(config){
3088     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3089 };
3090
3091 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3092     
3093     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3094     
3095     getAutoCreate : function(){
3096         
3097         
3098         return  {
3099             tag: 'div',
3100             cls: 'sidebar sidebar-nav'
3101         };
3102     
3103         
3104     }
3105     
3106     
3107     
3108 });
3109
3110
3111
3112  
3113
3114  /*
3115  * - LGPL
3116  *
3117  * nav group
3118  * 
3119  */
3120
3121 /**
3122  * @class Roo.bootstrap.NavGroup
3123  * @extends Roo.bootstrap.Component
3124  * Bootstrap NavGroup class
3125  * @cfg {String} align left | right
3126  * @cfg {Boolean} inverse false | true
3127  * @cfg {String} type (nav|pills|tab) default nav
3128  * @cfg {String} navId - reference Id for navbar.
3129
3130  * 
3131  * @constructor
3132  * Create a new nav group
3133  * @param {Object} config The config object
3134  */
3135
3136 Roo.bootstrap.NavGroup = function(config){
3137     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3138     this.navItems = [];
3139     Roo.bootstrap.NavGroup.register(this);
3140      this.addEvents({
3141         /**
3142              * @event changed
3143              * Fires when the active item changes
3144              * @param {Roo.bootstrap.NavGroup} this
3145              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3146              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3147          */
3148         'changed': true
3149      });
3150     
3151 };
3152
3153 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3154     
3155     align: '',
3156     inverse: false,
3157     form: false,
3158     type: 'nav',
3159     navId : '',
3160     // private
3161     
3162     navItems : false,
3163     
3164     getAutoCreate : function()
3165     {
3166         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3167         
3168         cfg = {
3169             tag : 'ul',
3170             cls: 'nav' 
3171         }
3172         
3173         if (['tabs','pills'].indexOf(this.type)!==-1) {
3174             cfg.cls += ' nav-' + this.type
3175         } else {
3176             if (this.type!=='nav') {
3177                 Roo.log('nav type must be nav/tabs/pills')
3178             }
3179             cfg.cls += ' navbar-nav'
3180         }
3181         
3182         if (this.parent().sidebar) {
3183             cfg = {
3184                 tag: 'ul',
3185                 cls: 'dashboard-menu sidebar-menu'
3186             }
3187             
3188             return cfg;
3189         }
3190         
3191         if (this.form === true) {
3192             cfg = {
3193                 tag: 'form',
3194                 cls: 'navbar-form'
3195             }
3196             
3197             if (this.align === 'right') {
3198                 cfg.cls += ' navbar-right';
3199             } else {
3200                 cfg.cls += ' navbar-left';
3201             }
3202         }
3203         
3204         if (this.align === 'right') {
3205             cfg.cls += ' navbar-right';
3206         }
3207         
3208         if (this.inverse) {
3209             cfg.cls += ' navbar-inverse';
3210             
3211         }
3212         
3213         
3214         return cfg;
3215     },
3216     
3217     setActiveItem : function(item)
3218     {
3219         var prev = false;
3220         Roo.each(this.navItems, function(v){
3221             if (v == item) {
3222                 return ;
3223             }
3224             if (v.isActive()) {
3225                 v.setActive(false, true);
3226                 prev = v;
3227                 
3228             }
3229             
3230         });
3231
3232         item.setActive(true, true);
3233         this.fireEvent('changed', this, item, prev);
3234         
3235         
3236     },
3237     
3238     addItem : function(cfg)
3239     {
3240         var cn = new Roo.bootstrap.NavItem(cfg);
3241         this.register(cn);
3242         cn.parentId = this.id;
3243         cn.onRender(this.el, null);
3244         return cn;
3245     },
3246     
3247     register : function(item)
3248     {
3249         this.navItems.push( item);
3250         item.navId = this.navId;
3251     
3252     },
3253     getNavItem: function(tabId)
3254     {
3255         var ret = false;
3256         Roo.each(this.navItems, function(e) {
3257             if (e.tabId == tabId) {
3258                ret =  e;
3259                return false;
3260             }
3261             return true;
3262             
3263         });
3264         return ret;
3265     }
3266     
3267     
3268     
3269     
3270 });
3271
3272  
3273 Roo.apply(Roo.bootstrap.NavGroup, {
3274     
3275     groups: {},
3276     
3277     register : function(navgrp)
3278     {
3279         this.groups[navgrp.navId] = navgrp;
3280         
3281     },
3282     get: function(navId) {
3283         return this.groups[navId];
3284     }
3285     
3286     
3287     
3288 });
3289
3290  /*
3291  * - LGPL
3292  *
3293  * row
3294  * 
3295  */
3296
3297 /**
3298  * @class Roo.bootstrap.NavItem
3299  * @extends Roo.bootstrap.Component
3300  * Bootstrap Navbar.NavItem class
3301  * @cfg {String} href  link to
3302  * @cfg {String} html content of button
3303  * @cfg {String} badge text inside badge
3304  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3305  * @cfg {String} glyphicon name of glyphicon
3306  * @cfg {String} icon name of font awesome icon
3307  * @cfg {Boolean} active Is item active
3308  * @cfg {Boolean} disabled Is item disabled
3309  
3310  * @cfg {Boolean} preventDefault (true | false) default false
3311  * @cfg {String} tabId the tab that this item activates.
3312  * @cfg {String} tagtype (a|span) render as a href or span?
3313   
3314  * @constructor
3315  * Create a new Navbar Item
3316  * @param {Object} config The config object
3317  */
3318 Roo.bootstrap.NavItem = function(config){
3319     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3320     this.addEvents({
3321         // raw events
3322         /**
3323          * @event click
3324          * The raw click event for the entire grid.
3325          * @param {Roo.EventObject} e
3326          */
3327         "click" : true,
3328          /**
3329             * @event changed
3330             * Fires when the active item active state changes
3331             * @param {Roo.bootstrap.NavItem} this
3332             * @param {boolean} state the new state
3333              
3334          */
3335         'changed': true
3336     });
3337    
3338 };
3339
3340 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3341     
3342     href: false,
3343     html: '',
3344     badge: '',
3345     icon: false,
3346     glyphicon: false,
3347     active: false,
3348     preventDefault : false,
3349     tabId : false,
3350     tagtype : 'a',
3351     disabled : false,
3352     
3353     getAutoCreate : function(){
3354          
3355         var cfg = {
3356             tag: 'li',
3357             cls: 'nav-item',
3358             cn : [
3359                 {
3360                     tag: this.tagtype,
3361                     href : this.href || "#",
3362                     html: this.html || ''
3363                 }
3364             ]
3365         }
3366             
3367         if (this.active) {
3368             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3369         }
3370             
3371         // glyphicon and icon go before content..
3372         if (this.glyphicon || this.icon) {
3373              if (this.icon) {
3374                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3375             } else {
3376                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3377             }
3378         }
3379         
3380         
3381         
3382         if (this.menu) {
3383             
3384             cfg.cn[0].html += " <span class='caret'></span>";
3385          
3386         }
3387         
3388         if (this.badge !== '') {
3389              
3390             cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3391         }
3392         if (this.disabled) {
3393             cfg.cls += ' disabled';
3394         }
3395         
3396         
3397         return cfg;
3398     },
3399     initEvents: function() {
3400        // Roo.log('init events?');
3401        // Roo.log(this.el.dom);
3402         if (typeof (this.menu) != 'undefined') {
3403             this.menu.parentType = this.xtype;
3404             this.menu.triggerEl = this.el;
3405             this.addxtype(Roo.apply({}, this.menu));
3406         }
3407
3408        
3409         this.el.select('a',true).on('click', this.onClick, this);
3410         // at this point parent should be available..
3411         this.parent().register(this);
3412     },
3413     
3414     onClick : function(e)
3415     {
3416          
3417         if(this.preventDefault){
3418             e.preventDefault();
3419         }
3420         if (this.disabled) {
3421             return;
3422         }
3423         Roo.log("fire event clicked");
3424         if(this.fireEvent('click', this, e) === false){
3425             return;
3426         };
3427         
3428         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3429             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3430                 this.parent().setActiveItem(this);
3431             }
3432             
3433             
3434             
3435         } 
3436     },
3437     
3438     isActive: function () {
3439         return this.active
3440     },
3441     setActive : function(state, fire)
3442     {
3443         this.active = state;
3444         if (!state ) {
3445             this.el.removeClass('active');
3446         } else if (!this.el.hasClass('active')) {
3447             this.el.addClass('active');
3448         }
3449         if (fire) {
3450             this.fireEvent('changed', this, state);
3451         }
3452         
3453         
3454     },
3455      // this should not be here...
3456     setDisabled : function(state)
3457     {
3458         this.disabled = state;
3459         if (!state ) {
3460             this.el.removeClass('disabled');
3461         } else if (!this.el.hasClass('disabled')) {
3462             this.el.addClass('disabled');
3463         }
3464         
3465     }
3466 });
3467  
3468
3469  /*
3470  * - LGPL
3471  *
3472  * sidebar item
3473  *
3474  *  li
3475  *    <span> icon </span>
3476  *    <span> text </span>
3477  *    <span>badge </span>
3478  */
3479
3480 /**
3481  * @class Roo.bootstrap.NavSidebarItem
3482  * @extends Roo.bootstrap.NavItem
3483  * Bootstrap Navbar.NavSidebarItem class
3484  * @constructor
3485  * Create a new Navbar Button
3486  * @param {Object} config The config object
3487  */
3488 Roo.bootstrap.NavSidebarItem = function(config){
3489     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3490     this.addEvents({
3491         // raw events
3492         /**
3493          * @event click
3494          * The raw click event for the entire grid.
3495          * @param {Roo.EventObject} e
3496          */
3497         "click" : true,
3498          /**
3499             * @event changed
3500             * Fires when the active item active state changes
3501             * @param {Roo.bootstrap.NavSidebarItem} this
3502             * @param {boolean} state the new state
3503              
3504          */
3505         'changed': true
3506     });
3507    
3508 };
3509
3510 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3511     
3512     
3513     getAutoCreate : function(){
3514         
3515         
3516         var a = {
3517                 tag: 'a',
3518                 href : this.href || '#',
3519                 cls: '',
3520                 html : '',
3521                 cn : []
3522         };
3523         var cfg = {
3524             tag: 'li',
3525             cls: '',
3526             cn: [ a ]
3527         }
3528         var span = {
3529             tag: 'span',
3530             html : this.html || ''
3531         }
3532         
3533         
3534         if (this.active) {
3535             cfg.cls += ' active';
3536         }
3537         
3538         // left icon..
3539         if (this.glyphicon || this.icon) {
3540             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3541             a.cn.push({ tag : 'i', cls : c }) ;
3542         }
3543         // html..
3544         a.cn.push(span);
3545         // then badge..
3546         if (this.badge !== '') {
3547             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3548         }
3549         // fi
3550         if (this.menu) {
3551             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3552             a.cls += 'dropdown-toggle treeview' ;
3553             
3554         }
3555         
3556         
3557         
3558         return cfg;
3559          
3560            
3561     }
3562    
3563      
3564  
3565 });
3566  
3567
3568  /*
3569  * - LGPL
3570  *
3571  * row
3572  * 
3573  */
3574
3575 /**
3576  * @class Roo.bootstrap.Row
3577  * @extends Roo.bootstrap.Component
3578  * Bootstrap Row class (contains columns...)
3579  * 
3580  * @constructor
3581  * Create a new Row
3582  * @param {Object} config The config object
3583  */
3584
3585 Roo.bootstrap.Row = function(config){
3586     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3587 };
3588
3589 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3590     
3591     getAutoCreate : function(){
3592        return {
3593             cls: 'row clearfix'
3594        };
3595     }
3596     
3597     
3598 });
3599
3600  
3601
3602  /*
3603  * - LGPL
3604  *
3605  * element
3606  * 
3607  */
3608
3609 /**
3610  * @class Roo.bootstrap.Element
3611  * @extends Roo.bootstrap.Component
3612  * Bootstrap Element class
3613  * @cfg {String} html contents of the element
3614  * @cfg {String} tag tag of the element
3615  * @cfg {String} cls class of the element
3616  * 
3617  * @constructor
3618  * Create a new Element
3619  * @param {Object} config The config object
3620  */
3621
3622 Roo.bootstrap.Element = function(config){
3623     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3624 };
3625
3626 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3627     
3628     tag: 'div',
3629     cls: '',
3630     html: '',
3631      
3632     
3633     getAutoCreate : function(){
3634         
3635         var cfg = {
3636             tag: this.tag,
3637             cls: this.cls,
3638             html: this.html
3639         }
3640         
3641         
3642         
3643         return cfg;
3644     }
3645    
3646 });
3647
3648  
3649
3650  /*
3651  * - LGPL
3652  *
3653  * pagination
3654  * 
3655  */
3656
3657 /**
3658  * @class Roo.bootstrap.Pagination
3659  * @extends Roo.bootstrap.Component
3660  * Bootstrap Pagination class
3661  * @cfg {String} size xs | sm | md | lg
3662  * @cfg {Boolean} inverse false | true
3663  * 
3664  * @constructor
3665  * Create a new Pagination
3666  * @param {Object} config The config object
3667  */
3668
3669 Roo.bootstrap.Pagination = function(config){
3670     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3671 };
3672
3673 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3674     
3675     cls: false,
3676     size: false,
3677     inverse: false,
3678     
3679     getAutoCreate : function(){
3680         var cfg = {
3681             tag: 'ul',
3682                 cls: 'pagination'
3683         };
3684         if (this.inverse) {
3685             cfg.cls += ' inverse';
3686         }
3687         if (this.html) {
3688             cfg.html=this.html;
3689         }
3690         if (this.cls) {
3691             cfg.cls += " " + this.cls;
3692         }
3693         return cfg;
3694     }
3695    
3696 });
3697
3698  
3699
3700  /*
3701  * - LGPL
3702  *
3703  * Pagination item
3704  * 
3705  */
3706
3707
3708 /**
3709  * @class Roo.bootstrap.PaginationItem
3710  * @extends Roo.bootstrap.Component
3711  * Bootstrap PaginationItem class
3712  * @cfg {String} html text
3713  * @cfg {String} href the link
3714  * @cfg {Boolean} preventDefault (true | false) default true
3715  * @cfg {Boolean} active (true | false) default false
3716  * 
3717  * 
3718  * @constructor
3719  * Create a new PaginationItem
3720  * @param {Object} config The config object
3721  */
3722
3723
3724 Roo.bootstrap.PaginationItem = function(config){
3725     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3726     this.addEvents({
3727         // raw events
3728         /**
3729          * @event click
3730          * The raw click event for the entire grid.
3731          * @param {Roo.EventObject} e
3732          */
3733         "click" : true
3734     });
3735 };
3736
3737 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3738     
3739     href : false,
3740     html : false,
3741     preventDefault: true,
3742     active : false,
3743     cls : false,
3744     
3745     getAutoCreate : function(){
3746         var cfg= {
3747             tag: 'li',
3748             cn: [
3749                 {
3750                     tag : 'a',
3751                     href : this.href ? this.href : '#',
3752                     html : this.html ? this.html : ''
3753                 }
3754             ]
3755         };
3756         
3757         if(this.cls){
3758             cfg.cls = this.cls;
3759         }
3760         
3761         if(this.active){
3762             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3763         }
3764         
3765         return cfg;
3766     },
3767     
3768     initEvents: function() {
3769         
3770         this.el.on('click', this.onClick, this);
3771         
3772     },
3773     onClick : function(e)
3774     {
3775         Roo.log('PaginationItem on click ');
3776         if(this.preventDefault){
3777             e.preventDefault();
3778         }
3779         
3780         this.fireEvent('click', this, e);
3781     }
3782    
3783 });
3784
3785  
3786
3787  /*
3788  * - LGPL
3789  *
3790  * slider
3791  * 
3792  */
3793
3794
3795 /**
3796  * @class Roo.bootstrap.Slider
3797  * @extends Roo.bootstrap.Component
3798  * Bootstrap Slider class
3799  *    
3800  * @constructor
3801  * Create a new Slider
3802  * @param {Object} config The config object
3803  */
3804
3805 Roo.bootstrap.Slider = function(config){
3806     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3807 };
3808
3809 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3810     
3811     getAutoCreate : function(){
3812         
3813         var cfg = {
3814             tag: 'div',
3815             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3816             cn: [
3817                 {
3818                     tag: 'a',
3819                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3820                 }
3821             ]
3822         }
3823         
3824         return cfg;
3825     }
3826    
3827 });
3828
3829  /*
3830  * Based on:
3831  * Ext JS Library 1.1.1
3832  * Copyright(c) 2006-2007, Ext JS, LLC.
3833  *
3834  * Originally Released Under LGPL - original licence link has changed is not relivant.
3835  *
3836  * Fork - LGPL
3837  * <script type="text/javascript">
3838  */
3839  
3840
3841 /**
3842  * @class Roo.grid.ColumnModel
3843  * @extends Roo.util.Observable
3844  * This is the default implementation of a ColumnModel used by the Grid. It defines
3845  * the columns in the grid.
3846  * <br>Usage:<br>
3847  <pre><code>
3848  var colModel = new Roo.grid.ColumnModel([
3849         {header: "Ticker", width: 60, sortable: true, locked: true},
3850         {header: "Company Name", width: 150, sortable: true},
3851         {header: "Market Cap.", width: 100, sortable: true},
3852         {header: "$ Sales", width: 100, sortable: true, renderer: money},
3853         {header: "Employees", width: 100, sortable: true, resizable: false}
3854  ]);
3855  </code></pre>
3856  * <p>
3857  
3858  * The config options listed for this class are options which may appear in each
3859  * individual column definition.
3860  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
3861  * @constructor
3862  * @param {Object} config An Array of column config objects. See this class's
3863  * config objects for details.
3864 */
3865 Roo.grid.ColumnModel = function(config){
3866         /**
3867      * The config passed into the constructor
3868      */
3869     this.config = config;
3870     this.lookup = {};
3871
3872     // if no id, create one
3873     // if the column does not have a dataIndex mapping,
3874     // map it to the order it is in the config
3875     for(var i = 0, len = config.length; i < len; i++){
3876         var c = config[i];
3877         if(typeof c.dataIndex == "undefined"){
3878             c.dataIndex = i;
3879         }
3880         if(typeof c.renderer == "string"){
3881             c.renderer = Roo.util.Format[c.renderer];
3882         }
3883         if(typeof c.id == "undefined"){
3884             c.id = Roo.id();
3885         }
3886         if(c.editor && c.editor.xtype){
3887             c.editor  = Roo.factory(c.editor, Roo.grid);
3888         }
3889         if(c.editor && c.editor.isFormField){
3890             c.editor = new Roo.grid.GridEditor(c.editor);
3891         }
3892         this.lookup[c.id] = c;
3893     }
3894
3895     /**
3896      * The width of columns which have no width specified (defaults to 100)
3897      * @type Number
3898      */
3899     this.defaultWidth = 100;
3900
3901     /**
3902      * Default sortable of columns which have no sortable specified (defaults to false)
3903      * @type Boolean
3904      */
3905     this.defaultSortable = false;
3906
3907     this.addEvents({
3908         /**
3909              * @event widthchange
3910              * Fires when the width of a column changes.
3911              * @param {ColumnModel} this
3912              * @param {Number} columnIndex The column index
3913              * @param {Number} newWidth The new width
3914              */
3915             "widthchange": true,
3916         /**
3917              * @event headerchange
3918              * Fires when the text of a header changes.
3919              * @param {ColumnModel} this
3920              * @param {Number} columnIndex The column index
3921              * @param {Number} newText The new header text
3922              */
3923             "headerchange": true,
3924         /**
3925              * @event hiddenchange
3926              * Fires when a column is hidden or "unhidden".
3927              * @param {ColumnModel} this
3928              * @param {Number} columnIndex The column index
3929              * @param {Boolean} hidden true if hidden, false otherwise
3930              */
3931             "hiddenchange": true,
3932             /**
3933          * @event columnmoved
3934          * Fires when a column is moved.
3935          * @param {ColumnModel} this
3936          * @param {Number} oldIndex
3937          * @param {Number} newIndex
3938          */
3939         "columnmoved" : true,
3940         /**
3941          * @event columlockchange
3942          * Fires when a column's locked state is changed
3943          * @param {ColumnModel} this
3944          * @param {Number} colIndex
3945          * @param {Boolean} locked true if locked
3946          */
3947         "columnlockchange" : true
3948     });
3949     Roo.grid.ColumnModel.superclass.constructor.call(this);
3950 };
3951 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
3952     /**
3953      * @cfg {String} header The header text to display in the Grid view.
3954      */
3955     /**
3956      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
3957      * {@link Roo.data.Record} definition from which to draw the column's value. If not
3958      * specified, the column's index is used as an index into the Record's data Array.
3959      */
3960     /**
3961      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
3962      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
3963      */
3964     /**
3965      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
3966      * Defaults to the value of the {@link #defaultSortable} property.
3967      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
3968      */
3969     /**
3970      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
3971      */
3972     /**
3973      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
3974      */
3975     /**
3976      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
3977      */
3978     /**
3979      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
3980      */
3981     /**
3982      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
3983      * given the cell's data value. See {@link #setRenderer}. If not specified, the
3984      * default renderer uses the raw data value.
3985      */
3986        /**
3987      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
3988      */
3989     /**
3990      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
3991      */
3992
3993     /**
3994      * Returns the id of the column at the specified index.
3995      * @param {Number} index The column index
3996      * @return {String} the id
3997      */
3998     getColumnId : function(index){
3999         return this.config[index].id;
4000     },
4001
4002     /**
4003      * Returns the column for a specified id.
4004      * @param {String} id The column id
4005      * @return {Object} the column
4006      */
4007     getColumnById : function(id){
4008         return this.lookup[id];
4009     },
4010
4011     
4012     /**
4013      * Returns the column for a specified dataIndex.
4014      * @param {String} dataIndex The column dataIndex
4015      * @return {Object|Boolean} the column or false if not found
4016      */
4017     getColumnByDataIndex: function(dataIndex){
4018         var index = this.findColumnIndex(dataIndex);
4019         return index > -1 ? this.config[index] : false;
4020     },
4021     
4022     /**
4023      * Returns the index for a specified column id.
4024      * @param {String} id The column id
4025      * @return {Number} the index, or -1 if not found
4026      */
4027     getIndexById : function(id){
4028         for(var i = 0, len = this.config.length; i < len; i++){
4029             if(this.config[i].id == id){
4030                 return i;
4031             }
4032         }
4033         return -1;
4034     },
4035     
4036     /**
4037      * Returns the index for a specified column dataIndex.
4038      * @param {String} dataIndex The column dataIndex
4039      * @return {Number} the index, or -1 if not found
4040      */
4041     
4042     findColumnIndex : function(dataIndex){
4043         for(var i = 0, len = this.config.length; i < len; i++){
4044             if(this.config[i].dataIndex == dataIndex){
4045                 return i;
4046             }
4047         }
4048         return -1;
4049     },
4050     
4051     
4052     moveColumn : function(oldIndex, newIndex){
4053         var c = this.config[oldIndex];
4054         this.config.splice(oldIndex, 1);
4055         this.config.splice(newIndex, 0, c);
4056         this.dataMap = null;
4057         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4058     },
4059
4060     isLocked : function(colIndex){
4061         return this.config[colIndex].locked === true;
4062     },
4063
4064     setLocked : function(colIndex, value, suppressEvent){
4065         if(this.isLocked(colIndex) == value){
4066             return;
4067         }
4068         this.config[colIndex].locked = value;
4069         if(!suppressEvent){
4070             this.fireEvent("columnlockchange", this, colIndex, value);
4071         }
4072     },
4073
4074     getTotalLockedWidth : function(){
4075         var totalWidth = 0;
4076         for(var i = 0; i < this.config.length; i++){
4077             if(this.isLocked(i) && !this.isHidden(i)){
4078                 this.totalWidth += this.getColumnWidth(i);
4079             }
4080         }
4081         return totalWidth;
4082     },
4083
4084     getLockedCount : function(){
4085         for(var i = 0, len = this.config.length; i < len; i++){
4086             if(!this.isLocked(i)){
4087                 return i;
4088             }
4089         }
4090     },
4091
4092     /**
4093      * Returns the number of columns.
4094      * @return {Number}
4095      */
4096     getColumnCount : function(visibleOnly){
4097         if(visibleOnly === true){
4098             var c = 0;
4099             for(var i = 0, len = this.config.length; i < len; i++){
4100                 if(!this.isHidden(i)){
4101                     c++;
4102                 }
4103             }
4104             return c;
4105         }
4106         return this.config.length;
4107     },
4108
4109     /**
4110      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4111      * @param {Function} fn
4112      * @param {Object} scope (optional)
4113      * @return {Array} result
4114      */
4115     getColumnsBy : function(fn, scope){
4116         var r = [];
4117         for(var i = 0, len = this.config.length; i < len; i++){
4118             var c = this.config[i];
4119             if(fn.call(scope||this, c, i) === true){
4120                 r[r.length] = c;
4121             }
4122         }
4123         return r;
4124     },
4125
4126     /**
4127      * Returns true if the specified column is sortable.
4128      * @param {Number} col The column index
4129      * @return {Boolean}
4130      */
4131     isSortable : function(col){
4132         if(typeof this.config[col].sortable == "undefined"){
4133             return this.defaultSortable;
4134         }
4135         return this.config[col].sortable;
4136     },
4137
4138     /**
4139      * Returns the rendering (formatting) function defined for the column.
4140      * @param {Number} col The column index.
4141      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4142      */
4143     getRenderer : function(col){
4144         if(!this.config[col].renderer){
4145             return Roo.grid.ColumnModel.defaultRenderer;
4146         }
4147         return this.config[col].renderer;
4148     },
4149
4150     /**
4151      * Sets the rendering (formatting) function for a column.
4152      * @param {Number} col The column index
4153      * @param {Function} fn The function to use to process the cell's raw data
4154      * to return HTML markup for the grid view. The render function is called with
4155      * the following parameters:<ul>
4156      * <li>Data value.</li>
4157      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4158      * <li>css A CSS style string to apply to the table cell.</li>
4159      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4160      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4161      * <li>Row index</li>
4162      * <li>Column index</li>
4163      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4164      */
4165     setRenderer : function(col, fn){
4166         this.config[col].renderer = fn;
4167     },
4168
4169     /**
4170      * Returns the width for the specified column.
4171      * @param {Number} col The column index
4172      * @return {Number}
4173      */
4174     getColumnWidth : function(col){
4175         return this.config[col].width * 1 || this.defaultWidth;
4176     },
4177
4178     /**
4179      * Sets the width for a column.
4180      * @param {Number} col The column index
4181      * @param {Number} width The new width
4182      */
4183     setColumnWidth : function(col, width, suppressEvent){
4184         this.config[col].width = width;
4185         this.totalWidth = null;
4186         if(!suppressEvent){
4187              this.fireEvent("widthchange", this, col, width);
4188         }
4189     },
4190
4191     /**
4192      * Returns the total width of all columns.
4193      * @param {Boolean} includeHidden True to include hidden column widths
4194      * @return {Number}
4195      */
4196     getTotalWidth : function(includeHidden){
4197         if(!this.totalWidth){
4198             this.totalWidth = 0;
4199             for(var i = 0, len = this.config.length; i < len; i++){
4200                 if(includeHidden || !this.isHidden(i)){
4201                     this.totalWidth += this.getColumnWidth(i);
4202                 }
4203             }
4204         }
4205         return this.totalWidth;
4206     },
4207
4208     /**
4209      * Returns the header for the specified column.
4210      * @param {Number} col The column index
4211      * @return {String}
4212      */
4213     getColumnHeader : function(col){
4214         return this.config[col].header;
4215     },
4216
4217     /**
4218      * Sets the header for a column.
4219      * @param {Number} col The column index
4220      * @param {String} header The new header
4221      */
4222     setColumnHeader : function(col, header){
4223         this.config[col].header = header;
4224         this.fireEvent("headerchange", this, col, header);
4225     },
4226
4227     /**
4228      * Returns the tooltip for the specified column.
4229      * @param {Number} col The column index
4230      * @return {String}
4231      */
4232     getColumnTooltip : function(col){
4233             return this.config[col].tooltip;
4234     },
4235     /**
4236      * Sets the tooltip for a column.
4237      * @param {Number} col The column index
4238      * @param {String} tooltip The new tooltip
4239      */
4240     setColumnTooltip : function(col, tooltip){
4241             this.config[col].tooltip = tooltip;
4242     },
4243
4244     /**
4245      * Returns the dataIndex for the specified column.
4246      * @param {Number} col The column index
4247      * @return {Number}
4248      */
4249     getDataIndex : function(col){
4250         return this.config[col].dataIndex;
4251     },
4252
4253     /**
4254      * Sets the dataIndex for a column.
4255      * @param {Number} col The column index
4256      * @param {Number} dataIndex The new dataIndex
4257      */
4258     setDataIndex : function(col, dataIndex){
4259         this.config[col].dataIndex = dataIndex;
4260     },
4261
4262     
4263     
4264     /**
4265      * Returns true if the cell is editable.
4266      * @param {Number} colIndex The column index
4267      * @param {Number} rowIndex The row index
4268      * @return {Boolean}
4269      */
4270     isCellEditable : function(colIndex, rowIndex){
4271         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4272     },
4273
4274     /**
4275      * Returns the editor defined for the cell/column.
4276      * return false or null to disable editing.
4277      * @param {Number} colIndex The column index
4278      * @param {Number} rowIndex The row index
4279      * @return {Object}
4280      */
4281     getCellEditor : function(colIndex, rowIndex){
4282         return this.config[colIndex].editor;
4283     },
4284
4285     /**
4286      * Sets if a column is editable.
4287      * @param {Number} col The column index
4288      * @param {Boolean} editable True if the column is editable
4289      */
4290     setEditable : function(col, editable){
4291         this.config[col].editable = editable;
4292     },
4293
4294
4295     /**
4296      * Returns true if the column is hidden.
4297      * @param {Number} colIndex The column index
4298      * @return {Boolean}
4299      */
4300     isHidden : function(colIndex){
4301         return this.config[colIndex].hidden;
4302     },
4303
4304
4305     /**
4306      * Returns true if the column width cannot be changed
4307      */
4308     isFixed : function(colIndex){
4309         return this.config[colIndex].fixed;
4310     },
4311
4312     /**
4313      * Returns true if the column can be resized
4314      * @return {Boolean}
4315      */
4316     isResizable : function(colIndex){
4317         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4318     },
4319     /**
4320      * Sets if a column is hidden.
4321      * @param {Number} colIndex The column index
4322      * @param {Boolean} hidden True if the column is hidden
4323      */
4324     setHidden : function(colIndex, hidden){
4325         this.config[colIndex].hidden = hidden;
4326         this.totalWidth = null;
4327         this.fireEvent("hiddenchange", this, colIndex, hidden);
4328     },
4329
4330     /**
4331      * Sets the editor for a column.
4332      * @param {Number} col The column index
4333      * @param {Object} editor The editor object
4334      */
4335     setEditor : function(col, editor){
4336         this.config[col].editor = editor;
4337     }
4338 });
4339
4340 Roo.grid.ColumnModel.defaultRenderer = function(value){
4341         if(typeof value == "string" && value.length < 1){
4342             return "&#160;";
4343         }
4344         return value;
4345 };
4346
4347 // Alias for backwards compatibility
4348 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4349 /*
4350  * Based on:
4351  * Ext JS Library 1.1.1
4352  * Copyright(c) 2006-2007, Ext JS, LLC.
4353  *
4354  * Originally Released Under LGPL - original licence link has changed is not relivant.
4355  *
4356  * Fork - LGPL
4357  * <script type="text/javascript">
4358  */
4359  
4360 /**
4361  * @class Roo.LoadMask
4362  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4363  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4364  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4365  * element's UpdateManager load indicator and will be destroyed after the initial load.
4366  * @constructor
4367  * Create a new LoadMask
4368  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4369  * @param {Object} config The config object
4370  */
4371 Roo.LoadMask = function(el, config){
4372     this.el = Roo.get(el);
4373     Roo.apply(this, config);
4374     if(this.store){
4375         this.store.on('beforeload', this.onBeforeLoad, this);
4376         this.store.on('load', this.onLoad, this);
4377         this.store.on('loadexception', this.onLoadException, this);
4378         this.removeMask = false;
4379     }else{
4380         var um = this.el.getUpdateManager();
4381         um.showLoadIndicator = false; // disable the default indicator
4382         um.on('beforeupdate', this.onBeforeLoad, this);
4383         um.on('update', this.onLoad, this);
4384         um.on('failure', this.onLoad, this);
4385         this.removeMask = true;
4386     }
4387 };
4388
4389 Roo.LoadMask.prototype = {
4390     /**
4391      * @cfg {Boolean} removeMask
4392      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4393      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4394      */
4395     /**
4396      * @cfg {String} msg
4397      * The text to display in a centered loading message box (defaults to 'Loading...')
4398      */
4399     msg : 'Loading...',
4400     /**
4401      * @cfg {String} msgCls
4402      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4403      */
4404     msgCls : 'x-mask-loading',
4405
4406     /**
4407      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4408      * @type Boolean
4409      */
4410     disabled: false,
4411
4412     /**
4413      * Disables the mask to prevent it from being displayed
4414      */
4415     disable : function(){
4416        this.disabled = true;
4417     },
4418
4419     /**
4420      * Enables the mask so that it can be displayed
4421      */
4422     enable : function(){
4423         this.disabled = false;
4424     },
4425     
4426     onLoadException : function()
4427     {
4428         Roo.log(arguments);
4429         
4430         if (typeof(arguments[3]) != 'undefined') {
4431             Roo.MessageBox.alert("Error loading",arguments[3]);
4432         } 
4433         /*
4434         try {
4435             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4436                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4437             }   
4438         } catch(e) {
4439             
4440         }
4441         */
4442     
4443         
4444         
4445         this.el.unmask(this.removeMask);
4446     },
4447     // private
4448     onLoad : function()
4449     {
4450         this.el.unmask(this.removeMask);
4451     },
4452
4453     // private
4454     onBeforeLoad : function(){
4455         if(!this.disabled){
4456             this.el.mask(this.msg, this.msgCls);
4457         }
4458     },
4459
4460     // private
4461     destroy : function(){
4462         if(this.store){
4463             this.store.un('beforeload', this.onBeforeLoad, this);
4464             this.store.un('load', this.onLoad, this);
4465             this.store.un('loadexception', this.onLoadException, this);
4466         }else{
4467             var um = this.el.getUpdateManager();
4468             um.un('beforeupdate', this.onBeforeLoad, this);
4469             um.un('update', this.onLoad, this);
4470             um.un('failure', this.onLoad, this);
4471         }
4472     }
4473 };/*
4474  * - LGPL
4475  *
4476  * table
4477  * 
4478  */
4479
4480 /**
4481  * @class Roo.bootstrap.Table
4482  * @extends Roo.bootstrap.Component
4483  * Bootstrap Table class
4484  * @cfg {String} cls table class
4485  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4486  * @cfg {String} bgcolor Specifies the background color for a table
4487  * @cfg {Number} border Specifies whether the table cells should have borders or not
4488  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4489  * @cfg {Number} cellspacing Specifies the space between cells
4490  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4491  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4492  * @cfg {String} sortable Specifies that the table should be sortable
4493  * @cfg {String} summary Specifies a summary of the content of a table
4494  * @cfg {Number} width Specifies the width of a table
4495  * 
4496  * @cfg {boolean} striped Should the rows be alternative striped
4497  * @cfg {boolean} bordered Add borders to the table
4498  * @cfg {boolean} hover Add hover highlighting
4499  * @cfg {boolean} condensed Format condensed
4500  * @cfg {boolean} responsive Format condensed
4501  * @cfg {Boolean} loadMask (true|false) default false
4502  * @cfg {Boolean} tfoor (true|false) generate tfoot, default true
4503  * @cfg {Boolean} thead (true|false) generate thead, default true
4504  *
4505  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4506  
4507  * 
4508  * @constructor
4509  * Create a new Table
4510  * @param {Object} config The config object
4511  */
4512
4513 Roo.bootstrap.Table = function(config){
4514     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4515     
4516     if (this.sm) {
4517         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4518         this.sm = this.selModel;
4519         this.sm.xmodule = this.xmodule || false;
4520     }
4521     if (this.cm && typeof(this.cm.config) == 'undefined') {
4522         this.colModel = new Roo.grid.ColumnModel(this.cm);
4523         this.cm = this.colModel;
4524         this.cm.xmodule = this.xmodule || false;
4525     }
4526     if (this.store) {
4527         this.store= Roo.factory(this.store, Roo.data);
4528         this.ds = this.store;
4529         this.ds.xmodule = this.xmodule || false;
4530          
4531     }
4532     if (this.footer && this.store) {
4533         this.footer.dataSource = this.ds;
4534         this.footer = Roo.factory(this.footer);
4535     }
4536 };
4537
4538 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4539     
4540     cls: false,
4541     align: false,
4542     bgcolor: false,
4543     border: false,
4544     cellpadding: false,
4545     cellspacing: false,
4546     frame: false,
4547     rules: false,
4548     sortable: false,
4549     summary: false,
4550     width: false,
4551     striped : false,
4552     bordered: false,
4553     hover:  false,
4554     condensed : false,
4555     responsive : false,
4556     sm : false,
4557     cm : false,
4558     store : false,
4559     loadMask : false,
4560     tfoor : true,
4561     thead : true,
4562     
4563     getAutoCreate : function(){
4564         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4565         
4566         cfg = {
4567             tag: 'table',
4568             cls : 'table',
4569             cn : []
4570         }
4571             
4572         if (this.striped) {
4573             cfg.cls += ' table-striped';
4574         }
4575         if (this.hover) {
4576             cfg.cls += ' table-hover';
4577         }
4578         if (this.bordered) {
4579             cfg.cls += ' table-bordered';
4580         }
4581         if (this.condensed) {
4582             cfg.cls += ' table-condensed';
4583         }
4584         if (this.responsive) {
4585             cfg.cls += ' table-responsive';
4586         }
4587         
4588           
4589         
4590         
4591         if (this.cls) {
4592             cfg.cls+=  ' ' +this.cls;
4593         }
4594         
4595         // this lot should be simplifed...
4596         
4597         if (this.align) {
4598             cfg.align=this.align;
4599         }
4600         if (this.bgcolor) {
4601             cfg.bgcolor=this.bgcolor;
4602         }
4603         if (this.border) {
4604             cfg.border=this.border;
4605         }
4606         if (this.cellpadding) {
4607             cfg.cellpadding=this.cellpadding;
4608         }
4609         if (this.cellspacing) {
4610             cfg.cellspacing=this.cellspacing;
4611         }
4612         if (this.frame) {
4613             cfg.frame=this.frame;
4614         }
4615         if (this.rules) {
4616             cfg.rules=this.rules;
4617         }
4618         if (this.sortable) {
4619             cfg.sortable=this.sortable;
4620         }
4621         if (this.summary) {
4622             cfg.summary=this.summary;
4623         }
4624         if (this.width) {
4625             cfg.width=this.width;
4626         }
4627         
4628         if(this.store || this.cm){
4629             if(this.thead){
4630                 cfg.cn.push(this.renderHeader());
4631             }
4632             
4633             cfg.cn.push(this.renderBody());
4634             
4635             if(this.tfoot){
4636                 cfg.cn.push(this.renderFooter());
4637             }
4638             
4639             cfg.cls+=  ' TableGrid';
4640         }
4641         
4642         
4643         return { cn : [ cfg ] };
4644     },
4645 //    
4646 //    initTableGrid : function()
4647 //    {
4648 //        var cfg = {};
4649 //        
4650 //        var header = {
4651 //            tag: 'thead',
4652 //            cn : []
4653 //        };
4654 //        
4655 //        var cm = this.cm;
4656 //        
4657 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4658 //            header.cn.push({
4659 //                tag: 'th',
4660 //                html: cm.getColumnHeader(i)
4661 //            })
4662 //        }
4663 //        
4664 //        cfg.push(header);
4665 //        
4666 //        return cfg;
4667 //        
4668 //        
4669 //    },
4670     
4671     initEvents : function()
4672     {   
4673         if(!this.store || !this.cm){
4674             return;
4675         }
4676         
4677         Roo.log('initEvents with ds!!!!');
4678         
4679         var _this = this;
4680         
4681         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4682             e.on('click', _this.sort, _this);
4683         });
4684 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
4685 //        this.maskEl.enableDisplayMode("block");
4686 //        this.maskEl.show();
4687         
4688         this.parent().el.setStyle('position', 'relative');
4689         if (this.footer) {
4690             this.footer.parentId = this.id;
4691             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
4692         }
4693         
4694         
4695         // mask should be using Roo.bootstrap.Mask...
4696         /*
4697         var mark = {
4698             tag: "div",
4699             cls:"x-dlg-mask",
4700             style: "text-align:center",
4701             cn: [
4702                 {
4703                     tag: "div",
4704                     style: "background-color:white;width:50%;margin:100 auto",
4705                     cn: [
4706                         {
4707                             tag: "img",
4708                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
4709                         },
4710                         {
4711                             tag: "span",
4712                             html: "Loading"
4713                         }
4714                         
4715                     ]
4716                 }
4717             ]
4718         }
4719         
4720         var size = this.el.getSize();
4721         
4722         this.maskEl = Roo.DomHelper.append(document.body, mark, true);
4723         
4724         this.maskEl.setSize(size.width, 300); // we will fix the height at the beginning...
4725         this.maskEl.enableDisplayMode("block");
4726         */
4727         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
4728         
4729         this.store.on('load', this.onLoad, this);
4730         this.store.on('beforeload', this.onBeforeLoad, this);
4731         
4732         // load should be trigger on render..
4733         //this.store.load();
4734         
4735         
4736         
4737     },
4738     
4739     sort : function(e,el)
4740     {
4741         var col = Roo.get(el)
4742         
4743         if(!col.hasClass('sortable')){
4744             return;
4745         }
4746         
4747         var sort = col.attr('sort');
4748         var dir = 'ASC';
4749         
4750         if(col.hasClass('glyphicon-arrow-up')){
4751             dir = 'DESC';
4752         }
4753         
4754         this.store.sortInfo = {field : sort, direction : dir};
4755         
4756         if (this.footer) {
4757             Roo.log("calling footer first");
4758             this.footer.onClick('first');
4759         } else {
4760         
4761             this.store.load({ params : { start : 0 } });
4762         }
4763     },
4764     
4765     renderHeader : function()
4766     {
4767         var header = {
4768             tag: 'thead',
4769             cn : []
4770         };
4771         
4772         var cm = this.cm;
4773         
4774         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4775             
4776             var config = cm.config[i];
4777             
4778             if(typeof(config.hidden) != 'undefined' && config.hidden){
4779                 continue;
4780             }
4781                     
4782             var c = {
4783                 tag: 'th',
4784                 style : '',
4785                 html: cm.getColumnHeader(i)
4786             };
4787             
4788             if(typeof(config.dataIndex) != 'undefined'){
4789                 c.sort = config.dataIndex;
4790             }
4791             
4792             if(typeof(config.sortable) != 'undefined' && config.sortable){
4793                 c.cls = 'sortable';
4794             }
4795             
4796 //            if(typeof(config.align) != 'undefined' && config.align.length){
4797 //                c.style += ' text-align:' + config.align + ';';
4798 //            }
4799             
4800             if(typeof(config.width) != 'undefined'){
4801                 c.style += 'width:' + config.width + 'px;';
4802             }
4803             
4804             header.cn.push(c)
4805         }
4806         
4807         return header;
4808     },
4809     
4810     renderBody : function()
4811     {
4812         var body = {
4813             tag: 'tbody',
4814             cn : [
4815                 {
4816                     tag: 'tr',
4817                     cn : [
4818                         {
4819                             tag : 'td',
4820                             colspan :  this.cm.getColumnCount()
4821                         }
4822                     ]
4823                 }
4824             ]
4825         };
4826         
4827         return body;
4828     },
4829     
4830     renderFooter : function()
4831     {
4832         var footer = {
4833             tag: 'tfoot',
4834             cn : [
4835                 {
4836                     tag: 'tr',
4837                     cn : [
4838                         {
4839                             tag : 'td',
4840                             colspan :  this.cm.getColumnCount()
4841                         }
4842                     ]
4843                 }
4844             ]
4845         };
4846         
4847         return footer;
4848     },
4849     
4850     onLoad : function()
4851     {
4852         Roo.log('ds onload');
4853         this.clear();
4854         
4855         var _this = this;
4856         var cm = this.cm;
4857         
4858         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4859             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
4860             
4861             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
4862                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
4863             }
4864             
4865             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
4866                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
4867             }
4868         });
4869         
4870         var tbody = this.el.select('tbody', true).first();
4871         
4872         var renders = [];
4873                     
4874         if(this.store.getCount() > 0){
4875             this.store.data.each(function(d){
4876                 var row = {
4877                     tag : 'tr',
4878                     cn : []
4879                 };
4880                 
4881                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4882                     var config = cm.config[i];
4883                     
4884                     if(typeof(config.hidden) != 'undefined' && config.hidden){
4885                         continue;
4886                     }
4887                     
4888                     var renderer = cm.getRenderer(i);
4889                     var value = '';
4890                     var id = Roo.id();
4891                     
4892                     if(typeof(renderer) !== 'undefined'){
4893                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
4894                     }
4895                     
4896                     if(typeof(value) === 'object'){
4897                         renders.push({
4898                             id : id,
4899                             cfg : value 
4900                         })
4901                     }
4902                     
4903                     var td = {
4904                         tag: 'td',
4905                         id: id,
4906                         style: '',
4907                         html: (typeof(value) === 'object') ? '' : value
4908                     };
4909                     
4910                     if(typeof(config.align) != 'undefined' && config.align.length){
4911                         td.style += ' text-align:' + config.align + ';';
4912                     }
4913                     
4914                     if(typeof(config.width) != 'undefined'){
4915                         td.style += 'width:' +  config.width + 'px;';
4916                     }
4917                     
4918                     
4919                     row.cn.push(td);
4920                    
4921                 }
4922                 
4923                 tbody.createChild(row);
4924                 
4925             });
4926         }
4927         
4928         
4929         if(renders.length){
4930             var _this = this;
4931             Roo.each(renders, function(r){
4932                 _this.renderColumn(r);
4933             })
4934         }
4935
4936         //if(this.loadMask){
4937         //    this.maskEl.hide();
4938         //}
4939     },
4940     
4941     onBeforeLoad : function()
4942     {
4943         //Roo.log('ds onBeforeLoad');
4944         
4945         //this.clear();
4946         
4947         //if(this.loadMask){
4948         //    this.maskEl.show();
4949         //}
4950     },
4951     
4952     clear : function()
4953     {
4954         this.el.select('tbody', true).first().dom.innerHTML = '';
4955     },
4956     
4957     getSelectionModel : function(){
4958         if(!this.selModel){
4959             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
4960         }
4961         return this.selModel;
4962     },
4963     
4964     renderColumn : function(r)
4965     {
4966         var _this = this;
4967         r.cfg.render(Roo.get(r.id));
4968         
4969         if(r.cfg.cn){
4970             Roo.each(r.cfg.cn, function(c){
4971                 var child = {
4972                     id: r.id,
4973                     cfg: c
4974                 }
4975                 _this.renderColumn(child);
4976             })
4977         }
4978     }
4979    
4980 });
4981
4982  
4983
4984  /*
4985  * - LGPL
4986  *
4987  * table cell
4988  * 
4989  */
4990
4991 /**
4992  * @class Roo.bootstrap.TableCell
4993  * @extends Roo.bootstrap.Component
4994  * Bootstrap TableCell class
4995  * @cfg {String} html cell contain text
4996  * @cfg {String} cls cell class
4997  * @cfg {String} tag cell tag (td|th) default td
4998  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
4999  * @cfg {String} align Aligns the content in a cell
5000  * @cfg {String} axis Categorizes cells
5001  * @cfg {String} bgcolor Specifies the background color of a cell
5002  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5003  * @cfg {Number} colspan Specifies the number of columns a cell should span
5004  * @cfg {String} headers Specifies one or more header cells a cell is related to
5005  * @cfg {Number} height Sets the height of a cell
5006  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5007  * @cfg {Number} rowspan Sets the number of rows a cell should span
5008  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5009  * @cfg {String} valign Vertical aligns the content in a cell
5010  * @cfg {Number} width Specifies the width of a cell
5011  * 
5012  * @constructor
5013  * Create a new TableCell
5014  * @param {Object} config The config object
5015  */
5016
5017 Roo.bootstrap.TableCell = function(config){
5018     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5019 };
5020
5021 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5022     
5023     html: false,
5024     cls: false,
5025     tag: false,
5026     abbr: false,
5027     align: false,
5028     axis: false,
5029     bgcolor: false,
5030     charoff: false,
5031     colspan: false,
5032     headers: false,
5033     height: false,
5034     nowrap: false,
5035     rowspan: false,
5036     scope: false,
5037     valign: false,
5038     width: false,
5039     
5040     
5041     getAutoCreate : function(){
5042         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5043         
5044         cfg = {
5045             tag: 'td'
5046         }
5047         
5048         if(this.tag){
5049             cfg.tag = this.tag;
5050         }
5051         
5052         if (this.html) {
5053             cfg.html=this.html
5054         }
5055         if (this.cls) {
5056             cfg.cls=this.cls
5057         }
5058         if (this.abbr) {
5059             cfg.abbr=this.abbr
5060         }
5061         if (this.align) {
5062             cfg.align=this.align
5063         }
5064         if (this.axis) {
5065             cfg.axis=this.axis
5066         }
5067         if (this.bgcolor) {
5068             cfg.bgcolor=this.bgcolor
5069         }
5070         if (this.charoff) {
5071             cfg.charoff=this.charoff
5072         }
5073         if (this.colspan) {
5074             cfg.colspan=this.colspan
5075         }
5076         if (this.headers) {
5077             cfg.headers=this.headers
5078         }
5079         if (this.height) {
5080             cfg.height=this.height
5081         }
5082         if (this.nowrap) {
5083             cfg.nowrap=this.nowrap
5084         }
5085         if (this.rowspan) {
5086             cfg.rowspan=this.rowspan
5087         }
5088         if (this.scope) {
5089             cfg.scope=this.scope
5090         }
5091         if (this.valign) {
5092             cfg.valign=this.valign
5093         }
5094         if (this.width) {
5095             cfg.width=this.width
5096         }
5097         
5098         
5099         return cfg;
5100     }
5101    
5102 });
5103
5104  
5105
5106  /*
5107  * - LGPL
5108  *
5109  * table row
5110  * 
5111  */
5112
5113 /**
5114  * @class Roo.bootstrap.TableRow
5115  * @extends Roo.bootstrap.Component
5116  * Bootstrap TableRow class
5117  * @cfg {String} cls row class
5118  * @cfg {String} align Aligns the content in a table row
5119  * @cfg {String} bgcolor Specifies a background color for a table row
5120  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5121  * @cfg {String} valign Vertical aligns the content in a table row
5122  * 
5123  * @constructor
5124  * Create a new TableRow
5125  * @param {Object} config The config object
5126  */
5127
5128 Roo.bootstrap.TableRow = function(config){
5129     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5130 };
5131
5132 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5133     
5134     cls: false,
5135     align: false,
5136     bgcolor: false,
5137     charoff: false,
5138     valign: false,
5139     
5140     getAutoCreate : function(){
5141         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5142         
5143         cfg = {
5144             tag: 'tr'
5145         }
5146             
5147         if(this.cls){
5148             cfg.cls = this.cls;
5149         }
5150         if(this.align){
5151             cfg.align = this.align;
5152         }
5153         if(this.bgcolor){
5154             cfg.bgcolor = this.bgcolor;
5155         }
5156         if(this.charoff){
5157             cfg.charoff = this.charoff;
5158         }
5159         if(this.valign){
5160             cfg.valign = this.valign;
5161         }
5162         
5163         return cfg;
5164     }
5165    
5166 });
5167
5168  
5169
5170  /*
5171  * - LGPL
5172  *
5173  * table body
5174  * 
5175  */
5176
5177 /**
5178  * @class Roo.bootstrap.TableBody
5179  * @extends Roo.bootstrap.Component
5180  * Bootstrap TableBody class
5181  * @cfg {String} cls element class
5182  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5183  * @cfg {String} align Aligns the content inside the element
5184  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5185  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5186  * 
5187  * @constructor
5188  * Create a new TableBody
5189  * @param {Object} config The config object
5190  */
5191
5192 Roo.bootstrap.TableBody = function(config){
5193     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5194 };
5195
5196 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5197     
5198     cls: false,
5199     tag: false,
5200     align: false,
5201     charoff: false,
5202     valign: false,
5203     
5204     getAutoCreate : function(){
5205         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5206         
5207         cfg = {
5208             tag: 'tbody'
5209         }
5210             
5211         if (this.cls) {
5212             cfg.cls=this.cls
5213         }
5214         if(this.tag){
5215             cfg.tag = this.tag;
5216         }
5217         
5218         if(this.align){
5219             cfg.align = this.align;
5220         }
5221         if(this.charoff){
5222             cfg.charoff = this.charoff;
5223         }
5224         if(this.valign){
5225             cfg.valign = this.valign;
5226         }
5227         
5228         return cfg;
5229     }
5230     
5231     
5232 //    initEvents : function()
5233 //    {
5234 //        
5235 //        if(!this.store){
5236 //            return;
5237 //        }
5238 //        
5239 //        this.store = Roo.factory(this.store, Roo.data);
5240 //        this.store.on('load', this.onLoad, this);
5241 //        
5242 //        this.store.load();
5243 //        
5244 //    },
5245 //    
5246 //    onLoad: function () 
5247 //    {   
5248 //        this.fireEvent('load', this);
5249 //    }
5250 //    
5251 //   
5252 });
5253
5254  
5255
5256  /*
5257  * Based on:
5258  * Ext JS Library 1.1.1
5259  * Copyright(c) 2006-2007, Ext JS, LLC.
5260  *
5261  * Originally Released Under LGPL - original licence link has changed is not relivant.
5262  *
5263  * Fork - LGPL
5264  * <script type="text/javascript">
5265  */
5266
5267 // as we use this in bootstrap.
5268 Roo.namespace('Roo.form');
5269  /**
5270  * @class Roo.form.Action
5271  * Internal Class used to handle form actions
5272  * @constructor
5273  * @param {Roo.form.BasicForm} el The form element or its id
5274  * @param {Object} config Configuration options
5275  */
5276
5277  
5278  
5279 // define the action interface
5280 Roo.form.Action = function(form, options){
5281     this.form = form;
5282     this.options = options || {};
5283 };
5284 /**
5285  * Client Validation Failed
5286  * @const 
5287  */
5288 Roo.form.Action.CLIENT_INVALID = 'client';
5289 /**
5290  * Server Validation Failed
5291  * @const 
5292  */
5293 Roo.form.Action.SERVER_INVALID = 'server';
5294  /**
5295  * Connect to Server Failed
5296  * @const 
5297  */
5298 Roo.form.Action.CONNECT_FAILURE = 'connect';
5299 /**
5300  * Reading Data from Server Failed
5301  * @const 
5302  */
5303 Roo.form.Action.LOAD_FAILURE = 'load';
5304
5305 Roo.form.Action.prototype = {
5306     type : 'default',
5307     failureType : undefined,
5308     response : undefined,
5309     result : undefined,
5310
5311     // interface method
5312     run : function(options){
5313
5314     },
5315
5316     // interface method
5317     success : function(response){
5318
5319     },
5320
5321     // interface method
5322     handleResponse : function(response){
5323
5324     },
5325
5326     // default connection failure
5327     failure : function(response){
5328         
5329         this.response = response;
5330         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5331         this.form.afterAction(this, false);
5332     },
5333
5334     processResponse : function(response){
5335         this.response = response;
5336         if(!response.responseText){
5337             return true;
5338         }
5339         this.result = this.handleResponse(response);
5340         return this.result;
5341     },
5342
5343     // utility functions used internally
5344     getUrl : function(appendParams){
5345         var url = this.options.url || this.form.url || this.form.el.dom.action;
5346         if(appendParams){
5347             var p = this.getParams();
5348             if(p){
5349                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5350             }
5351         }
5352         return url;
5353     },
5354
5355     getMethod : function(){
5356         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5357     },
5358
5359     getParams : function(){
5360         var bp = this.form.baseParams;
5361         var p = this.options.params;
5362         if(p){
5363             if(typeof p == "object"){
5364                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5365             }else if(typeof p == 'string' && bp){
5366                 p += '&' + Roo.urlEncode(bp);
5367             }
5368         }else if(bp){
5369             p = Roo.urlEncode(bp);
5370         }
5371         return p;
5372     },
5373
5374     createCallback : function(){
5375         return {
5376             success: this.success,
5377             failure: this.failure,
5378             scope: this,
5379             timeout: (this.form.timeout*1000),
5380             upload: this.form.fileUpload ? this.success : undefined
5381         };
5382     }
5383 };
5384
5385 Roo.form.Action.Submit = function(form, options){
5386     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5387 };
5388
5389 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5390     type : 'submit',
5391
5392     haveProgress : false,
5393     uploadComplete : false,
5394     
5395     // uploadProgress indicator.
5396     uploadProgress : function()
5397     {
5398         if (!this.form.progressUrl) {
5399             return;
5400         }
5401         
5402         if (!this.haveProgress) {
5403             Roo.MessageBox.progress("Uploading", "Uploading");
5404         }
5405         if (this.uploadComplete) {
5406            Roo.MessageBox.hide();
5407            return;
5408         }
5409         
5410         this.haveProgress = true;
5411    
5412         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5413         
5414         var c = new Roo.data.Connection();
5415         c.request({
5416             url : this.form.progressUrl,
5417             params: {
5418                 id : uid
5419             },
5420             method: 'GET',
5421             success : function(req){
5422                //console.log(data);
5423                 var rdata = false;
5424                 var edata;
5425                 try  {
5426                    rdata = Roo.decode(req.responseText)
5427                 } catch (e) {
5428                     Roo.log("Invalid data from server..");
5429                     Roo.log(edata);
5430                     return;
5431                 }
5432                 if (!rdata || !rdata.success) {
5433                     Roo.log(rdata);
5434                     Roo.MessageBox.alert(Roo.encode(rdata));
5435                     return;
5436                 }
5437                 var data = rdata.data;
5438                 
5439                 if (this.uploadComplete) {
5440                    Roo.MessageBox.hide();
5441                    return;
5442                 }
5443                    
5444                 if (data){
5445                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5446                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5447                     );
5448                 }
5449                 this.uploadProgress.defer(2000,this);
5450             },
5451        
5452             failure: function(data) {
5453                 Roo.log('progress url failed ');
5454                 Roo.log(data);
5455             },
5456             scope : this
5457         });
5458            
5459     },
5460     
5461     
5462     run : function()
5463     {
5464         // run get Values on the form, so it syncs any secondary forms.
5465         this.form.getValues();
5466         
5467         var o = this.options;
5468         var method = this.getMethod();
5469         var isPost = method == 'POST';
5470         if(o.clientValidation === false || this.form.isValid()){
5471             
5472             if (this.form.progressUrl) {
5473                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5474                     (new Date() * 1) + '' + Math.random());
5475                     
5476             } 
5477             
5478             
5479             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5480                 form:this.form.el.dom,
5481                 url:this.getUrl(!isPost),
5482                 method: method,
5483                 params:isPost ? this.getParams() : null,
5484                 isUpload: this.form.fileUpload
5485             }));
5486             
5487             this.uploadProgress();
5488
5489         }else if (o.clientValidation !== false){ // client validation failed
5490             this.failureType = Roo.form.Action.CLIENT_INVALID;
5491             this.form.afterAction(this, false);
5492         }
5493     },
5494
5495     success : function(response)
5496     {
5497         this.uploadComplete= true;
5498         if (this.haveProgress) {
5499             Roo.MessageBox.hide();
5500         }
5501         
5502         
5503         var result = this.processResponse(response);
5504         if(result === true || result.success){
5505             this.form.afterAction(this, true);
5506             return;
5507         }
5508         if(result.errors){
5509             this.form.markInvalid(result.errors);
5510             this.failureType = Roo.form.Action.SERVER_INVALID;
5511         }
5512         this.form.afterAction(this, false);
5513     },
5514     failure : function(response)
5515     {
5516         this.uploadComplete= true;
5517         if (this.haveProgress) {
5518             Roo.MessageBox.hide();
5519         }
5520         
5521         this.response = response;
5522         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5523         this.form.afterAction(this, false);
5524     },
5525     
5526     handleResponse : function(response){
5527         if(this.form.errorReader){
5528             var rs = this.form.errorReader.read(response);
5529             var errors = [];
5530             if(rs.records){
5531                 for(var i = 0, len = rs.records.length; i < len; i++) {
5532                     var r = rs.records[i];
5533                     errors[i] = r.data;
5534                 }
5535             }
5536             if(errors.length < 1){
5537                 errors = null;
5538             }
5539             return {
5540                 success : rs.success,
5541                 errors : errors
5542             };
5543         }
5544         var ret = false;
5545         try {
5546             ret = Roo.decode(response.responseText);
5547         } catch (e) {
5548             ret = {
5549                 success: false,
5550                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5551                 errors : []
5552             };
5553         }
5554         return ret;
5555         
5556     }
5557 });
5558
5559
5560 Roo.form.Action.Load = function(form, options){
5561     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5562     this.reader = this.form.reader;
5563 };
5564
5565 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5566     type : 'load',
5567
5568     run : function(){
5569         
5570         Roo.Ajax.request(Roo.apply(
5571                 this.createCallback(), {
5572                     method:this.getMethod(),
5573                     url:this.getUrl(false),
5574                     params:this.getParams()
5575         }));
5576     },
5577
5578     success : function(response){
5579         
5580         var result = this.processResponse(response);
5581         if(result === true || !result.success || !result.data){
5582             this.failureType = Roo.form.Action.LOAD_FAILURE;
5583             this.form.afterAction(this, false);
5584             return;
5585         }
5586         this.form.clearInvalid();
5587         this.form.setValues(result.data);
5588         this.form.afterAction(this, true);
5589     },
5590
5591     handleResponse : function(response){
5592         if(this.form.reader){
5593             var rs = this.form.reader.read(response);
5594             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5595             return {
5596                 success : rs.success,
5597                 data : data
5598             };
5599         }
5600         return Roo.decode(response.responseText);
5601     }
5602 });
5603
5604 Roo.form.Action.ACTION_TYPES = {
5605     'load' : Roo.form.Action.Load,
5606     'submit' : Roo.form.Action.Submit
5607 };/*
5608  * - LGPL
5609  *
5610  * form
5611  * 
5612  */
5613
5614 /**
5615  * @class Roo.bootstrap.Form
5616  * @extends Roo.bootstrap.Component
5617  * Bootstrap Form class
5618  * @cfg {String} method  GET | POST (default POST)
5619  * @cfg {String} labelAlign top | left (default top)
5620   * @cfg {String} align left  | right - for navbars
5621
5622  * 
5623  * @constructor
5624  * Create a new Form
5625  * @param {Object} config The config object
5626  */
5627
5628
5629 Roo.bootstrap.Form = function(config){
5630     Roo.bootstrap.Form.superclass.constructor.call(this, config);
5631     this.addEvents({
5632         /**
5633          * @event clientvalidation
5634          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5635          * @param {Form} this
5636          * @param {Boolean} valid true if the form has passed client-side validation
5637          */
5638         clientvalidation: true,
5639         /**
5640          * @event beforeaction
5641          * Fires before any action is performed. Return false to cancel the action.
5642          * @param {Form} this
5643          * @param {Action} action The action to be performed
5644          */
5645         beforeaction: true,
5646         /**
5647          * @event actionfailed
5648          * Fires when an action fails.
5649          * @param {Form} this
5650          * @param {Action} action The action that failed
5651          */
5652         actionfailed : true,
5653         /**
5654          * @event actioncomplete
5655          * Fires when an action is completed.
5656          * @param {Form} this
5657          * @param {Action} action The action that completed
5658          */
5659         actioncomplete : true
5660     });
5661     
5662 };
5663
5664 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
5665       
5666      /**
5667      * @cfg {String} method
5668      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
5669      */
5670     method : 'POST',
5671     /**
5672      * @cfg {String} url
5673      * The URL to use for form actions if one isn't supplied in the action options.
5674      */
5675     /**
5676      * @cfg {Boolean} fileUpload
5677      * Set to true if this form is a file upload.
5678      */
5679      
5680     /**
5681      * @cfg {Object} baseParams
5682      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
5683      */
5684       
5685     /**
5686      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5687      */
5688     timeout: 30,
5689     /**
5690      * @cfg {Sting} align (left|right) for navbar forms
5691      */
5692     align : 'left',
5693
5694     // private
5695     activeAction : null,
5696  
5697     /**
5698      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5699      * element by passing it or its id or mask the form itself by passing in true.
5700      * @type Mixed
5701      */
5702     waitMsgTarget : false,
5703     
5704      
5705     
5706     /**
5707      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5708      * element by passing it or its id or mask the form itself by passing in true.
5709      * @type Mixed
5710      */
5711     
5712     getAutoCreate : function(){
5713         
5714         var cfg = {
5715             tag: 'form',
5716             method : this.method || 'POST',
5717             id : this.id || Roo.id(),
5718             cls : ''
5719         }
5720         if (this.parent().xtype.match(/^Nav/)) {
5721             cfg.cls = 'navbar-form navbar-' + this.align;
5722             
5723         }
5724         
5725         if (this.labelAlign == 'left' ) {
5726             cfg.cls += ' form-horizontal';
5727         }
5728         
5729         
5730         return cfg;
5731     },
5732     initEvents : function()
5733     {
5734         this.el.on('submit', this.onSubmit, this);
5735         
5736         
5737     },
5738     // private
5739     onSubmit : function(e){
5740         e.stopEvent();
5741     },
5742     
5743      /**
5744      * Returns true if client-side validation on the form is successful.
5745      * @return Boolean
5746      */
5747     isValid : function(){
5748         var items = this.getItems();
5749         var valid = true;
5750         items.each(function(f){
5751            if(!f.validate()){
5752                valid = false;
5753                
5754            }
5755         });
5756         return valid;
5757     },
5758     /**
5759      * Returns true if any fields in this form have changed since their original load.
5760      * @return Boolean
5761      */
5762     isDirty : function(){
5763         var dirty = false;
5764         var items = this.getItems();
5765         items.each(function(f){
5766            if(f.isDirty()){
5767                dirty = true;
5768                return false;
5769            }
5770            return true;
5771         });
5772         return dirty;
5773     },
5774      /**
5775      * Performs a predefined action (submit or load) or custom actions you define on this form.
5776      * @param {String} actionName The name of the action type
5777      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
5778      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
5779      * accept other config options):
5780      * <pre>
5781 Property          Type             Description
5782 ----------------  ---------------  ----------------------------------------------------------------------------------
5783 url               String           The url for the action (defaults to the form's url)
5784 method            String           The form method to use (defaults to the form's method, or POST if not defined)
5785 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
5786 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
5787                                    validate the form on the client (defaults to false)
5788      * </pre>
5789      * @return {BasicForm} this
5790      */
5791     doAction : function(action, options){
5792         if(typeof action == 'string'){
5793             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
5794         }
5795         if(this.fireEvent('beforeaction', this, action) !== false){
5796             this.beforeAction(action);
5797             action.run.defer(100, action);
5798         }
5799         return this;
5800     },
5801     
5802     // private
5803     beforeAction : function(action){
5804         var o = action.options;
5805         
5806         // not really supported yet.. ??
5807         
5808         //if(this.waitMsgTarget === true){
5809             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
5810         //}else if(this.waitMsgTarget){
5811         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
5812         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
5813         //}else {
5814         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
5815        // }
5816          
5817     },
5818
5819     // private
5820     afterAction : function(action, success){
5821         this.activeAction = null;
5822         var o = action.options;
5823         
5824         //if(this.waitMsgTarget === true){
5825             this.el.unmask();
5826         //}else if(this.waitMsgTarget){
5827         //    this.waitMsgTarget.unmask();
5828         //}else{
5829         //    Roo.MessageBox.updateProgress(1);
5830         //    Roo.MessageBox.hide();
5831        // }
5832         // 
5833         if(success){
5834             if(o.reset){
5835                 this.reset();
5836             }
5837             Roo.callback(o.success, o.scope, [this, action]);
5838             this.fireEvent('actioncomplete', this, action);
5839             
5840         }else{
5841             
5842             // failure condition..
5843             // we have a scenario where updates need confirming.
5844             // eg. if a locking scenario exists..
5845             // we look for { errors : { needs_confirm : true }} in the response.
5846             if (
5847                 (typeof(action.result) != 'undefined')  &&
5848                 (typeof(action.result.errors) != 'undefined')  &&
5849                 (typeof(action.result.errors.needs_confirm) != 'undefined')
5850            ){
5851                 var _t = this;
5852                 Roo.log("not supported yet");
5853                  /*
5854                 
5855                 Roo.MessageBox.confirm(
5856                     "Change requires confirmation",
5857                     action.result.errorMsg,
5858                     function(r) {
5859                         if (r != 'yes') {
5860                             return;
5861                         }
5862                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
5863                     }
5864                     
5865                 );
5866                 */
5867                 
5868                 
5869                 return;
5870             }
5871             
5872             Roo.callback(o.failure, o.scope, [this, action]);
5873             // show an error message if no failed handler is set..
5874             if (!this.hasListener('actionfailed')) {
5875                 Roo.log("need to add dialog support");
5876                 /*
5877                 Roo.MessageBox.alert("Error",
5878                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
5879                         action.result.errorMsg :
5880                         "Saving Failed, please check your entries or try again"
5881                 );
5882                 */
5883             }
5884             
5885             this.fireEvent('actionfailed', this, action);
5886         }
5887         
5888     },
5889     /**
5890      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
5891      * @param {String} id The value to search for
5892      * @return Field
5893      */
5894     findField : function(id){
5895         var items = this.getItems();
5896         var field = items.get(id);
5897         if(!field){
5898              items.each(function(f){
5899                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
5900                     field = f;
5901                     return false;
5902                 }
5903                 return true;
5904             });
5905         }
5906         return field || null;
5907     },
5908      /**
5909      * Mark fields in this form invalid in bulk.
5910      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
5911      * @return {BasicForm} this
5912      */
5913     markInvalid : function(errors){
5914         if(errors instanceof Array){
5915             for(var i = 0, len = errors.length; i < len; i++){
5916                 var fieldError = errors[i];
5917                 var f = this.findField(fieldError.id);
5918                 if(f){
5919                     f.markInvalid(fieldError.msg);
5920                 }
5921             }
5922         }else{
5923             var field, id;
5924             for(id in errors){
5925                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
5926                     field.markInvalid(errors[id]);
5927                 }
5928             }
5929         }
5930         //Roo.each(this.childForms || [], function (f) {
5931         //    f.markInvalid(errors);
5932         //});
5933         
5934         return this;
5935     },
5936
5937     /**
5938      * Set values for fields in this form in bulk.
5939      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
5940      * @return {BasicForm} this
5941      */
5942     setValues : function(values){
5943         if(values instanceof Array){ // array of objects
5944             for(var i = 0, len = values.length; i < len; i++){
5945                 var v = values[i];
5946                 var f = this.findField(v.id);
5947                 if(f){
5948                     f.setValue(v.value);
5949                     if(this.trackResetOnLoad){
5950                         f.originalValue = f.getValue();
5951                     }
5952                 }
5953             }
5954         }else{ // object hash
5955             var field, id;
5956             for(id in values){
5957                 if(typeof values[id] != 'function' && (field = this.findField(id))){
5958                     
5959                     if (field.setFromData && 
5960                         field.valueField && 
5961                         field.displayField &&
5962                         // combos' with local stores can 
5963                         // be queried via setValue()
5964                         // to set their value..
5965                         (field.store && !field.store.isLocal)
5966                         ) {
5967                         // it's a combo
5968                         var sd = { };
5969                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
5970                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
5971                         field.setFromData(sd);
5972                         
5973                     } else {
5974                         field.setValue(values[id]);
5975                     }
5976                     
5977                     
5978                     if(this.trackResetOnLoad){
5979                         field.originalValue = field.getValue();
5980                     }
5981                 }
5982             }
5983         }
5984          
5985         //Roo.each(this.childForms || [], function (f) {
5986         //    f.setValues(values);
5987         //});
5988                 
5989         return this;
5990     },
5991
5992     /**
5993      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
5994      * they are returned as an array.
5995      * @param {Boolean} asString
5996      * @return {Object}
5997      */
5998     getValues : function(asString){
5999         //if (this.childForms) {
6000             // copy values from the child forms
6001         //    Roo.each(this.childForms, function (f) {
6002         //        this.setValues(f.getValues());
6003         //    }, this);
6004         //}
6005         
6006         
6007         
6008         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6009         if(asString === true){
6010             return fs;
6011         }
6012         return Roo.urlDecode(fs);
6013     },
6014     
6015     /**
6016      * Returns the fields in this form as an object with key/value pairs. 
6017      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6018      * @return {Object}
6019      */
6020     getFieldValues : function(with_hidden)
6021     {
6022         var items = this.getItems();
6023         var ret = {};
6024         items.each(function(f){
6025             if (!f.getName()) {
6026                 return;
6027             }
6028             var v = f.getValue();
6029             if (f.inputType =='radio') {
6030                 if (typeof(ret[f.getName()]) == 'undefined') {
6031                     ret[f.getName()] = ''; // empty..
6032                 }
6033                 
6034                 if (!f.el.dom.checked) {
6035                     return;
6036                     
6037                 }
6038                 v = f.el.dom.value;
6039                 
6040             }
6041             
6042             // not sure if this supported any more..
6043             if ((typeof(v) == 'object') && f.getRawValue) {
6044                 v = f.getRawValue() ; // dates..
6045             }
6046             // combo boxes where name != hiddenName...
6047             if (f.name != f.getName()) {
6048                 ret[f.name] = f.getRawValue();
6049             }
6050             ret[f.getName()] = v;
6051         });
6052         
6053         return ret;
6054     },
6055
6056     /**
6057      * Clears all invalid messages in this form.
6058      * @return {BasicForm} this
6059      */
6060     clearInvalid : function(){
6061         var items = this.getItems();
6062         
6063         items.each(function(f){
6064            f.clearInvalid();
6065         });
6066         
6067         
6068         
6069         return this;
6070     },
6071
6072     /**
6073      * Resets this form.
6074      * @return {BasicForm} this
6075      */
6076     reset : function(){
6077         var items = this.getItems();
6078         items.each(function(f){
6079             f.reset();
6080         });
6081         
6082         Roo.each(this.childForms || [], function (f) {
6083             f.reset();
6084         });
6085        
6086         
6087         return this;
6088     },
6089     getItems : function()
6090     {
6091         var r=new Roo.util.MixedCollection(false, function(o){
6092             return o.id || (o.id = Roo.id());
6093         });
6094         var iter = function(el) {
6095             if (el.inputEl) {
6096                 r.add(el);
6097             }
6098             if (!el.items) {
6099                 return;
6100             }
6101             Roo.each(el.items,function(e) {
6102                 iter(e);
6103             });
6104             
6105             
6106         };
6107         iter(this);
6108         return r;
6109         
6110         
6111         
6112         
6113     }
6114     
6115 });
6116
6117  
6118 /*
6119  * Based on:
6120  * Ext JS Library 1.1.1
6121  * Copyright(c) 2006-2007, Ext JS, LLC.
6122  *
6123  * Originally Released Under LGPL - original licence link has changed is not relivant.
6124  *
6125  * Fork - LGPL
6126  * <script type="text/javascript">
6127  */
6128 /**
6129  * @class Roo.form.VTypes
6130  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6131  * @singleton
6132  */
6133 Roo.form.VTypes = function(){
6134     // closure these in so they are only created once.
6135     var alpha = /^[a-zA-Z_]+$/;
6136     var alphanum = /^[a-zA-Z0-9_]+$/;
6137     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6138     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6139
6140     // All these messages and functions are configurable
6141     return {
6142         /**
6143          * The function used to validate email addresses
6144          * @param {String} value The email address
6145          */
6146         'email' : function(v){
6147             return email.test(v);
6148         },
6149         /**
6150          * The error text to display when the email validation function returns false
6151          * @type String
6152          */
6153         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6154         /**
6155          * The keystroke filter mask to be applied on email input
6156          * @type RegExp
6157          */
6158         'emailMask' : /[a-z0-9_\.\-@]/i,
6159
6160         /**
6161          * The function used to validate URLs
6162          * @param {String} value The URL
6163          */
6164         'url' : function(v){
6165             return url.test(v);
6166         },
6167         /**
6168          * The error text to display when the url validation function returns false
6169          * @type String
6170          */
6171         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6172         
6173         /**
6174          * The function used to validate alpha values
6175          * @param {String} value The value
6176          */
6177         'alpha' : function(v){
6178             return alpha.test(v);
6179         },
6180         /**
6181          * The error text to display when the alpha validation function returns false
6182          * @type String
6183          */
6184         'alphaText' : 'This field should only contain letters and _',
6185         /**
6186          * The keystroke filter mask to be applied on alpha input
6187          * @type RegExp
6188          */
6189         'alphaMask' : /[a-z_]/i,
6190
6191         /**
6192          * The function used to validate alphanumeric values
6193          * @param {String} value The value
6194          */
6195         'alphanum' : function(v){
6196             return alphanum.test(v);
6197         },
6198         /**
6199          * The error text to display when the alphanumeric validation function returns false
6200          * @type String
6201          */
6202         'alphanumText' : 'This field should only contain letters, numbers and _',
6203         /**
6204          * The keystroke filter mask to be applied on alphanumeric input
6205          * @type RegExp
6206          */
6207         'alphanumMask' : /[a-z0-9_]/i
6208     };
6209 }();/*
6210  * - LGPL
6211  *
6212  * Input
6213  * 
6214  */
6215
6216 /**
6217  * @class Roo.bootstrap.Input
6218  * @extends Roo.bootstrap.Component
6219  * Bootstrap Input class
6220  * @cfg {Boolean} disabled is it disabled
6221  * @cfg {String} fieldLabel - the label associated
6222  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6223  * @cfg {String} name name of the input
6224  * @cfg {string} fieldLabel - the label associated
6225  * @cfg {string}  inputType - input / file submit ...
6226  * @cfg {string} placeholder - placeholder to put in text.
6227  * @cfg {string}  before - input group add on before
6228  * @cfg {string} after - input group add on after
6229  * @cfg {string} size - (lg|sm) or leave empty..
6230  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6231  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6232  * @cfg {Number} md colspan out of 12 for computer-sized screens
6233  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6234  * @cfg {string} value default value of the input
6235  * @cfg {Number} labelWidth set the width of label (0-12)
6236  * @cfg {String} labelAlign (top|left)
6237  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6238  * @cfg {String} align (left|center|right) Default left
6239  * 
6240  * 
6241  * @constructor
6242  * Create a new Input
6243  * @param {Object} config The config object
6244  */
6245
6246 Roo.bootstrap.Input = function(config){
6247     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6248    
6249         this.addEvents({
6250             /**
6251              * @event focus
6252              * Fires when this field receives input focus.
6253              * @param {Roo.form.Field} this
6254              */
6255             focus : true,
6256             /**
6257              * @event blur
6258              * Fires when this field loses input focus.
6259              * @param {Roo.form.Field} this
6260              */
6261             blur : true,
6262             /**
6263              * @event specialkey
6264              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6265              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6266              * @param {Roo.form.Field} this
6267              * @param {Roo.EventObject} e The event object
6268              */
6269             specialkey : true,
6270             /**
6271              * @event change
6272              * Fires just before the field blurs if the field value has changed.
6273              * @param {Roo.form.Field} this
6274              * @param {Mixed} newValue The new value
6275              * @param {Mixed} oldValue The original value
6276              */
6277             change : true,
6278             /**
6279              * @event invalid
6280              * Fires after the field has been marked as invalid.
6281              * @param {Roo.form.Field} this
6282              * @param {String} msg The validation message
6283              */
6284             invalid : true,
6285             /**
6286              * @event valid
6287              * Fires after the field has been validated with no errors.
6288              * @param {Roo.form.Field} this
6289              */
6290             valid : true,
6291              /**
6292              * @event keyup
6293              * Fires after the key up
6294              * @param {Roo.form.Field} this
6295              * @param {Roo.EventObject}  e The event Object
6296              */
6297             keyup : true
6298         });
6299 };
6300
6301 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6302      /**
6303      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6304       automatic validation (defaults to "keyup").
6305      */
6306     validationEvent : "keyup",
6307      /**
6308      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6309      */
6310     validateOnBlur : true,
6311     /**
6312      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6313      */
6314     validationDelay : 250,
6315      /**
6316      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6317      */
6318     focusClass : "x-form-focus",  // not needed???
6319     
6320        
6321     /**
6322      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6323      */
6324     invalidClass : "has-error",
6325     
6326     /**
6327      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6328      */
6329     selectOnFocus : false,
6330     
6331      /**
6332      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6333      */
6334     maskRe : null,
6335        /**
6336      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6337      */
6338     vtype : null,
6339     
6340       /**
6341      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6342      */
6343     disableKeyFilter : false,
6344     
6345        /**
6346      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6347      */
6348     disabled : false,
6349      /**
6350      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6351      */
6352     allowBlank : true,
6353     /**
6354      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6355      */
6356     blankText : "This field is required",
6357     
6358      /**
6359      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6360      */
6361     minLength : 0,
6362     /**
6363      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6364      */
6365     maxLength : Number.MAX_VALUE,
6366     /**
6367      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6368      */
6369     minLengthText : "The minimum length for this field is {0}",
6370     /**
6371      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6372      */
6373     maxLengthText : "The maximum length for this field is {0}",
6374   
6375     
6376     /**
6377      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6378      * If available, this function will be called only after the basic validators all return true, and will be passed the
6379      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6380      */
6381     validator : null,
6382     /**
6383      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6384      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6385      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6386      */
6387     regex : null,
6388     /**
6389      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6390      */
6391     regexText : "",
6392     
6393     
6394     
6395     fieldLabel : '',
6396     inputType : 'text',
6397     
6398     name : false,
6399     placeholder: false,
6400     before : false,
6401     after : false,
6402     size : false,
6403     // private
6404     hasFocus : false,
6405     preventMark: false,
6406     isFormField : true,
6407     value : '',
6408     labelWidth : 2,
6409     labelAlign : false,
6410     readOnly : false,
6411     align : false,
6412     
6413     parentLabelAlign : function()
6414     {
6415         var parent = this;
6416         while (parent.parent()) {
6417             parent = parent.parent();
6418             if (typeof(parent.labelAlign) !='undefined') {
6419                 return parent.labelAlign;
6420             }
6421         }
6422         return 'left';
6423         
6424     },
6425     
6426     getAutoCreate : function(){
6427         
6428         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6429         
6430         var id = Roo.id();
6431         
6432         var cfg = {};
6433         
6434         if(this.inputType != 'hidden'){
6435             cfg.cls = 'form-group' //input-group
6436         }
6437         
6438         var input =  {
6439             tag: 'input',
6440             id : id,
6441             type : this.inputType,
6442             value : this.value,
6443             cls : 'form-control',
6444             placeholder : this.placeholder || ''
6445             
6446         };
6447         
6448         if(this.align){
6449             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6450         }
6451         
6452         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6453             input.maxLength = this.maxLength;
6454         }
6455         
6456         if (this.disabled) {
6457             input.disabled=true;
6458         }
6459         
6460         if (this.readOnly) {
6461             input.readonly=true;
6462         }
6463         
6464         if (this.name) {
6465             input.name = this.name;
6466         }
6467         if (this.size) {
6468             input.cls += ' input-' + this.size;
6469         }
6470         var settings=this;
6471         ['xs','sm','md','lg'].map(function(size){
6472             if (settings[size]) {
6473                 cfg.cls += ' col-' + size + '-' + settings[size];
6474             }
6475         });
6476         
6477         var inputblock = input;
6478         
6479         if (this.before || this.after) {
6480             
6481             inputblock = {
6482                 cls : 'input-group',
6483                 cn :  [] 
6484             };
6485             if (this.before && typeof(this.before) == 'string') {
6486                 
6487                 inputblock.cn.push({
6488                     tag :'span',
6489                     cls : 'roo-input-before input-group-addon',
6490                     html : this.before
6491                 });
6492             }
6493             if (this.before && typeof(this.before) == 'object') {
6494                 this.before = Roo.factory(this.before);
6495                 Roo.log(this.before);
6496                 inputblock.cn.push({
6497                     tag :'span',
6498                     cls : 'roo-input-before input-group-' +
6499                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6500                 });
6501             }
6502             
6503             inputblock.cn.push(input);
6504             
6505             if (this.after && typeof(this.after) == 'string') {
6506                 inputblock.cn.push({
6507                     tag :'span',
6508                     cls : 'roo-input-after input-group-addon',
6509                     html : this.after
6510                 });
6511             }
6512             if (this.after && typeof(this.after) == 'object') {
6513                 this.after = Roo.factory(this.after);
6514                 Roo.log(this.after);
6515                 inputblock.cn.push({
6516                     tag :'span',
6517                     cls : 'roo-input-after input-group-' +
6518                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6519                 });
6520             }
6521         };
6522         
6523         if (align ==='left' && this.fieldLabel.length) {
6524                 Roo.log("left and has label");
6525                 cfg.cn = [
6526                     
6527                     {
6528                         tag: 'label',
6529                         'for' :  id,
6530                         cls : 'control-label col-sm-' + this.labelWidth,
6531                         html : this.fieldLabel
6532                         
6533                     },
6534                     {
6535                         cls : "col-sm-" + (12 - this.labelWidth), 
6536                         cn: [
6537                             inputblock
6538                         ]
6539                     }
6540                     
6541                 ];
6542         } else if ( this.fieldLabel.length) {
6543                 Roo.log(" label");
6544                  cfg.cn = [
6545                    
6546                     {
6547                         tag: 'label',
6548                         //cls : 'input-group-addon',
6549                         html : this.fieldLabel
6550                         
6551                     },
6552                     
6553                     inputblock
6554                     
6555                 ];
6556
6557         } else {
6558             
6559                 Roo.log(" no label && no align");
6560                 cfg.cn = [
6561                     
6562                         inputblock
6563                     
6564                 ];
6565                 
6566                 
6567         };
6568         Roo.log('input-parentType: ' + this.parentType);
6569         
6570         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6571            cfg.cls += ' navbar-form';
6572            Roo.log(cfg);
6573         }
6574         
6575         return cfg;
6576         
6577     },
6578     /**
6579      * return the real input element.
6580      */
6581     inputEl: function ()
6582     {
6583         return this.el.select('input.form-control',true).first();
6584     },
6585     setDisabled : function(v)
6586     {
6587         var i  = this.inputEl().dom;
6588         if (!v) {
6589             i.removeAttribute('disabled');
6590             return;
6591             
6592         }
6593         i.setAttribute('disabled','true');
6594     },
6595     initEvents : function()
6596     {
6597         
6598         this.inputEl().on("keydown" , this.fireKey,  this);
6599         this.inputEl().on("focus", this.onFocus,  this);
6600         this.inputEl().on("blur", this.onBlur,  this);
6601         
6602         this.inputEl().relayEvent('keyup', this);
6603
6604         // reference to original value for reset
6605         this.originalValue = this.getValue();
6606         //Roo.form.TextField.superclass.initEvents.call(this);
6607         if(this.validationEvent == 'keyup'){
6608             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
6609             this.inputEl().on('keyup', this.filterValidation, this);
6610         }
6611         else if(this.validationEvent !== false){
6612             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
6613         }
6614         
6615         if(this.selectOnFocus){
6616             this.on("focus", this.preFocus, this);
6617             
6618         }
6619         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
6620             this.inputEl().on("keypress", this.filterKeys, this);
6621         }
6622        /* if(this.grow){
6623             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
6624             this.el.on("click", this.autoSize,  this);
6625         }
6626         */
6627         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
6628             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
6629         }
6630         
6631         if (typeof(this.before) == 'object') {
6632             this.before.render(this.el.select('.roo-input-before',true).first());
6633         }
6634         if (typeof(this.after) == 'object') {
6635             this.after.render(this.el.select('.roo-input-after',true).first());
6636         }
6637         
6638         
6639     },
6640     filterValidation : function(e){
6641         if(!e.isNavKeyPress()){
6642             this.validationTask.delay(this.validationDelay);
6643         }
6644     },
6645      /**
6646      * Validates the field value
6647      * @return {Boolean} True if the value is valid, else false
6648      */
6649     validate : function(){
6650         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
6651         if(this.disabled || this.validateValue(this.getRawValue())){
6652             this.clearInvalid();
6653             return true;
6654         }
6655         return false;
6656     },
6657     
6658     
6659     /**
6660      * Validates a value according to the field's validation rules and marks the field as invalid
6661      * if the validation fails
6662      * @param {Mixed} value The value to validate
6663      * @return {Boolean} True if the value is valid, else false
6664      */
6665     validateValue : function(value){
6666         if(value.length < 1)  { // if it's blank
6667              if(this.allowBlank){
6668                 this.clearInvalid();
6669                 return true;
6670              }else{
6671                 this.markInvalid(this.blankText);
6672                 return false;
6673              }
6674         }
6675         if(value.length < this.minLength){
6676             this.markInvalid(String.format(this.minLengthText, this.minLength));
6677             return false;
6678         }
6679         if(value.length > this.maxLength){
6680             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
6681             return false;
6682         }
6683         if(this.vtype){
6684             var vt = Roo.form.VTypes;
6685             if(!vt[this.vtype](value, this)){
6686                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
6687                 return false;
6688             }
6689         }
6690         if(typeof this.validator == "function"){
6691             var msg = this.validator(value);
6692             if(msg !== true){
6693                 this.markInvalid(msg);
6694                 return false;
6695             }
6696         }
6697         if(this.regex && !this.regex.test(value)){
6698             this.markInvalid(this.regexText);
6699             return false;
6700         }
6701         return true;
6702     },
6703
6704     
6705     
6706      // private
6707     fireKey : function(e){
6708         //Roo.log('field ' + e.getKey());
6709         if(e.isNavKeyPress()){
6710             this.fireEvent("specialkey", this, e);
6711         }
6712     },
6713     focus : function (selectText){
6714         if(this.rendered){
6715             this.inputEl().focus();
6716             if(selectText === true){
6717                 this.inputEl().dom.select();
6718             }
6719         }
6720         return this;
6721     } ,
6722     
6723     onFocus : function(){
6724         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6725            // this.el.addClass(this.focusClass);
6726         }
6727         if(!this.hasFocus){
6728             this.hasFocus = true;
6729             this.startValue = this.getValue();
6730             this.fireEvent("focus", this);
6731         }
6732     },
6733     
6734     beforeBlur : Roo.emptyFn,
6735
6736     
6737     // private
6738     onBlur : function(){
6739         this.beforeBlur();
6740         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6741             //this.el.removeClass(this.focusClass);
6742         }
6743         this.hasFocus = false;
6744         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
6745             this.validate();
6746         }
6747         var v = this.getValue();
6748         if(String(v) !== String(this.startValue)){
6749             this.fireEvent('change', this, v, this.startValue);
6750         }
6751         this.fireEvent("blur", this);
6752     },
6753     
6754     /**
6755      * Resets the current field value to the originally loaded value and clears any validation messages
6756      */
6757     reset : function(){
6758         this.setValue(this.originalValue);
6759         this.clearInvalid();
6760     },
6761      /**
6762      * Returns the name of the field
6763      * @return {Mixed} name The name field
6764      */
6765     getName: function(){
6766         return this.name;
6767     },
6768      /**
6769      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
6770      * @return {Mixed} value The field value
6771      */
6772     getValue : function(){
6773         return this.inputEl().getValue();
6774     },
6775     /**
6776      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
6777      * @return {Mixed} value The field value
6778      */
6779     getRawValue : function(){
6780         var v = this.inputEl().getValue();
6781         
6782         return v;
6783     },
6784     
6785     /**
6786      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
6787      * @param {Mixed} value The value to set
6788      */
6789     setRawValue : function(v){
6790         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6791     },
6792     
6793     selectText : function(start, end){
6794         var v = this.getRawValue();
6795         if(v.length > 0){
6796             start = start === undefined ? 0 : start;
6797             end = end === undefined ? v.length : end;
6798             var d = this.inputEl().dom;
6799             if(d.setSelectionRange){
6800                 d.setSelectionRange(start, end);
6801             }else if(d.createTextRange){
6802                 var range = d.createTextRange();
6803                 range.moveStart("character", start);
6804                 range.moveEnd("character", v.length-end);
6805                 range.select();
6806             }
6807         }
6808     },
6809     
6810     /**
6811      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
6812      * @param {Mixed} value The value to set
6813      */
6814     setValue : function(v){
6815         this.value = v;
6816         if(this.rendered){
6817             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6818             this.validate();
6819         }
6820     },
6821     
6822     /*
6823     processValue : function(value){
6824         if(this.stripCharsRe){
6825             var newValue = value.replace(this.stripCharsRe, '');
6826             if(newValue !== value){
6827                 this.setRawValue(newValue);
6828                 return newValue;
6829             }
6830         }
6831         return value;
6832     },
6833   */
6834     preFocus : function(){
6835         
6836         if(this.selectOnFocus){
6837             this.inputEl().dom.select();
6838         }
6839     },
6840     filterKeys : function(e){
6841         var k = e.getKey();
6842         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
6843             return;
6844         }
6845         var c = e.getCharCode(), cc = String.fromCharCode(c);
6846         if(Roo.isIE && (e.isSpecialKey() || !cc)){
6847             return;
6848         }
6849         if(!this.maskRe.test(cc)){
6850             e.stopEvent();
6851         }
6852     },
6853      /**
6854      * Clear any invalid styles/messages for this field
6855      */
6856     clearInvalid : function(){
6857         
6858         if(!this.el || this.preventMark){ // not rendered
6859             return;
6860         }
6861         this.el.removeClass(this.invalidClass);
6862         /*
6863         switch(this.msgTarget){
6864             case 'qtip':
6865                 this.el.dom.qtip = '';
6866                 break;
6867             case 'title':
6868                 this.el.dom.title = '';
6869                 break;
6870             case 'under':
6871                 if(this.errorEl){
6872                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
6873                 }
6874                 break;
6875             case 'side':
6876                 if(this.errorIcon){
6877                     this.errorIcon.dom.qtip = '';
6878                     this.errorIcon.hide();
6879                     this.un('resize', this.alignErrorIcon, this);
6880                 }
6881                 break;
6882             default:
6883                 var t = Roo.getDom(this.msgTarget);
6884                 t.innerHTML = '';
6885                 t.style.display = 'none';
6886                 break;
6887         }
6888         */
6889         this.fireEvent('valid', this);
6890     },
6891      /**
6892      * Mark this field as invalid
6893      * @param {String} msg The validation message
6894      */
6895     markInvalid : function(msg){
6896         if(!this.el  || this.preventMark){ // not rendered
6897             return;
6898         }
6899         this.el.addClass(this.invalidClass);
6900         /*
6901         msg = msg || this.invalidText;
6902         switch(this.msgTarget){
6903             case 'qtip':
6904                 this.el.dom.qtip = msg;
6905                 this.el.dom.qclass = 'x-form-invalid-tip';
6906                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
6907                     Roo.QuickTips.enable();
6908                 }
6909                 break;
6910             case 'title':
6911                 this.el.dom.title = msg;
6912                 break;
6913             case 'under':
6914                 if(!this.errorEl){
6915                     var elp = this.el.findParent('.x-form-element', 5, true);
6916                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
6917                     this.errorEl.setWidth(elp.getWidth(true)-20);
6918                 }
6919                 this.errorEl.update(msg);
6920                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
6921                 break;
6922             case 'side':
6923                 if(!this.errorIcon){
6924                     var elp = this.el.findParent('.x-form-element', 5, true);
6925                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
6926                 }
6927                 this.alignErrorIcon();
6928                 this.errorIcon.dom.qtip = msg;
6929                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
6930                 this.errorIcon.show();
6931                 this.on('resize', this.alignErrorIcon, this);
6932                 break;
6933             default:
6934                 var t = Roo.getDom(this.msgTarget);
6935                 t.innerHTML = msg;
6936                 t.style.display = this.msgDisplay;
6937                 break;
6938         }
6939         */
6940         this.fireEvent('invalid', this, msg);
6941     },
6942     // private
6943     SafariOnKeyDown : function(event)
6944     {
6945         // this is a workaround for a password hang bug on chrome/ webkit.
6946         
6947         var isSelectAll = false;
6948         
6949         if(this.inputEl().dom.selectionEnd > 0){
6950             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
6951         }
6952         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
6953             event.preventDefault();
6954             this.setValue('');
6955             return;
6956         }
6957         
6958         if(isSelectAll){ // backspace and delete key
6959             
6960             event.preventDefault();
6961             // this is very hacky as keydown always get's upper case.
6962             //
6963             var cc = String.fromCharCode(event.getCharCode());
6964             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
6965             
6966         }
6967     },
6968     adjustWidth : function(tag, w){
6969         tag = tag.toLowerCase();
6970         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
6971             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
6972                 if(tag == 'input'){
6973                     return w + 2;
6974                 }
6975                 if(tag == 'textarea'){
6976                     return w-2;
6977                 }
6978             }else if(Roo.isOpera){
6979                 if(tag == 'input'){
6980                     return w + 2;
6981                 }
6982                 if(tag == 'textarea'){
6983                     return w-2;
6984                 }
6985             }
6986         }
6987         return w;
6988     }
6989     
6990 });
6991
6992  
6993 /*
6994  * - LGPL
6995  *
6996  * Input
6997  * 
6998  */
6999
7000 /**
7001  * @class Roo.bootstrap.TextArea
7002  * @extends Roo.bootstrap.Input
7003  * Bootstrap TextArea class
7004  * @cfg {Number} cols Specifies the visible width of a text area
7005  * @cfg {Number} rows Specifies the visible number of lines in a text area
7006  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7007  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7008  * @cfg {string} html text
7009  * 
7010  * @constructor
7011  * Create a new TextArea
7012  * @param {Object} config The config object
7013  */
7014
7015 Roo.bootstrap.TextArea = function(config){
7016     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7017    
7018 };
7019
7020 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7021      
7022     cols : false,
7023     rows : 5,
7024     readOnly : false,
7025     warp : 'soft',
7026     resize : false,
7027     value: false,
7028     html: false,
7029     
7030     getAutoCreate : function(){
7031         
7032         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7033         
7034         var id = Roo.id();
7035         
7036         var cfg = {};
7037         
7038         var input =  {
7039             tag: 'textarea',
7040             id : id,
7041             warp : this.warp,
7042             rows : this.rows,
7043             value : this.value || '',
7044             html: this.html || '',
7045             cls : 'form-control',
7046             placeholder : this.placeholder || '' 
7047             
7048         };
7049         
7050         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7051             input.maxLength = this.maxLength;
7052         }
7053         
7054         if(this.resize){
7055             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7056         }
7057         
7058         if(this.cols){
7059             input.cols = this.cols;
7060         }
7061         
7062         if (this.readOnly) {
7063             input.readonly = true;
7064         }
7065         
7066         if (this.name) {
7067             input.name = this.name;
7068         }
7069         
7070         if (this.size) {
7071             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7072         }
7073         
7074         var settings=this;
7075         ['xs','sm','md','lg'].map(function(size){
7076             if (settings[size]) {
7077                 cfg.cls += ' col-' + size + '-' + settings[size];
7078             }
7079         });
7080         
7081         var inputblock = input;
7082         
7083         if (this.before || this.after) {
7084             
7085             inputblock = {
7086                 cls : 'input-group',
7087                 cn :  [] 
7088             };
7089             if (this.before) {
7090                 inputblock.cn.push({
7091                     tag :'span',
7092                     cls : 'input-group-addon',
7093                     html : this.before
7094                 });
7095             }
7096             inputblock.cn.push(input);
7097             if (this.after) {
7098                 inputblock.cn.push({
7099                     tag :'span',
7100                     cls : 'input-group-addon',
7101                     html : this.after
7102                 });
7103             }
7104             
7105         }
7106         
7107         if (align ==='left' && this.fieldLabel.length) {
7108                 Roo.log("left and has label");
7109                 cfg.cn = [
7110                     
7111                     {
7112                         tag: 'label',
7113                         'for' :  id,
7114                         cls : 'control-label col-sm-' + this.labelWidth,
7115                         html : this.fieldLabel
7116                         
7117                     },
7118                     {
7119                         cls : "col-sm-" + (12 - this.labelWidth), 
7120                         cn: [
7121                             inputblock
7122                         ]
7123                     }
7124                     
7125                 ];
7126         } else if ( this.fieldLabel.length) {
7127                 Roo.log(" label");
7128                  cfg.cn = [
7129                    
7130                     {
7131                         tag: 'label',
7132                         //cls : 'input-group-addon',
7133                         html : this.fieldLabel
7134                         
7135                     },
7136                     
7137                     inputblock
7138                     
7139                 ];
7140
7141         } else {
7142             
7143                    Roo.log(" no label && no align");
7144                 cfg.cn = [
7145                     
7146                         inputblock
7147                     
7148                 ];
7149                 
7150                 
7151         }
7152         
7153         if (this.disabled) {
7154             input.disabled=true;
7155         }
7156         
7157         return cfg;
7158         
7159     },
7160     /**
7161      * return the real textarea element.
7162      */
7163     inputEl: function ()
7164     {
7165         return this.el.select('textarea.form-control',true).first();
7166     }
7167 });
7168
7169  
7170 /*
7171  * - LGPL
7172  *
7173  * trigger field - base class for combo..
7174  * 
7175  */
7176  
7177 /**
7178  * @class Roo.bootstrap.TriggerField
7179  * @extends Roo.bootstrap.Input
7180  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7181  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7182  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7183  * for which you can provide a custom implementation.  For example:
7184  * <pre><code>
7185 var trigger = new Roo.bootstrap.TriggerField();
7186 trigger.onTriggerClick = myTriggerFn;
7187 trigger.applyTo('my-field');
7188 </code></pre>
7189  *
7190  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7191  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7192  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7193  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7194  * @constructor
7195  * Create a new TriggerField.
7196  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7197  * to the base TextField)
7198  */
7199 Roo.bootstrap.TriggerField = function(config){
7200     this.mimicing = false;
7201     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7202 };
7203
7204 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7205     /**
7206      * @cfg {String} triggerClass A CSS class to apply to the trigger
7207      */
7208      /**
7209      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7210      */
7211     hideTrigger:false,
7212
7213     /** @cfg {Boolean} grow @hide */
7214     /** @cfg {Number} growMin @hide */
7215     /** @cfg {Number} growMax @hide */
7216
7217     /**
7218      * @hide 
7219      * @method
7220      */
7221     autoSize: Roo.emptyFn,
7222     // private
7223     monitorTab : true,
7224     // private
7225     deferHeight : true,
7226
7227     
7228     actionMode : 'wrap',
7229     
7230     
7231     
7232     getAutoCreate : function(){
7233        
7234         var parent = this.parent();
7235         
7236         var align = this.labelAlign || this.parentLabelAlign();
7237         
7238         var id = Roo.id();
7239         
7240         var cfg = {
7241             cls: 'form-group' //input-group
7242         };
7243         
7244         
7245         var input =  {
7246             tag: 'input',
7247             id : id,
7248             type : this.inputType,
7249             cls : 'form-control',
7250             autocomplete: 'off',
7251             placeholder : this.placeholder || '' 
7252             
7253         };
7254         if (this.name) {
7255             input.name = this.name;
7256         }
7257         if (this.size) {
7258             input.cls += ' input-' + this.size;
7259         }
7260         
7261         if (this.disabled) {
7262             input.disabled=true;
7263         }
7264         
7265         var inputblock = input;
7266         
7267         if (this.before || this.after) {
7268             
7269             inputblock = {
7270                 cls : 'input-group',
7271                 cn :  [] 
7272             };
7273             if (this.before) {
7274                 inputblock.cn.push({
7275                     tag :'span',
7276                     cls : 'input-group-addon',
7277                     html : this.before
7278                 });
7279             }
7280             inputblock.cn.push(input);
7281             if (this.after) {
7282                 inputblock.cn.push({
7283                     tag :'span',
7284                     cls : 'input-group-addon',
7285                     html : this.after
7286                 });
7287             }
7288             
7289         };
7290         
7291         var box = {
7292             tag: 'div',
7293             cn: [
7294                 {
7295                     tag: 'input',
7296                     type : 'hidden',
7297                     cls: 'form-hidden-field'
7298                 },
7299                 inputblock
7300             ]
7301             
7302         };
7303         
7304         if(this.multiple){
7305             Roo.log('multiple');
7306             
7307             box = {
7308                 tag: 'div',
7309                 cn: [
7310                     {
7311                         tag: 'input',
7312                         type : 'hidden',
7313                         cls: 'form-hidden-field'
7314                     },
7315                     {
7316                         tag: 'ul',
7317                         cls: 'select2-choices',
7318                         cn:[
7319                             {
7320                                 tag: 'li',
7321                                 cls: 'select2-search-field',
7322                                 cn: [
7323
7324                                     inputblock
7325                                 ]
7326                             }
7327                         ]
7328                     }
7329                 ]
7330             }
7331         };
7332         
7333         var combobox = {
7334             cls: 'select2-container input-group',
7335             cn: [
7336                 box,
7337                 {
7338                     tag: 'ul',
7339                     cls: 'typeahead typeahead-long dropdown-menu',
7340                     style: 'display:none'
7341                 }
7342             ]
7343         };
7344         
7345         if(!this.multiple){
7346             combobox.cn.push({
7347                 tag :'span',
7348                 cls : 'input-group-addon btn dropdown-toggle',
7349                 cn : [
7350                     {
7351                         tag: 'span',
7352                         cls: 'caret'
7353                     },
7354                     {
7355                         tag: 'span',
7356                         cls: 'combobox-clear',
7357                         cn  : [
7358                             {
7359                                 tag : 'i',
7360                                 cls: 'icon-remove'
7361                             }
7362                         ]
7363                     }
7364                 ]
7365
7366             })
7367         }
7368         
7369         if(this.multiple){
7370             combobox.cls += ' select2-container-multi';
7371         }
7372         
7373         if (align ==='left' && this.fieldLabel.length) {
7374             
7375                 Roo.log("left and has label");
7376                 cfg.cn = [
7377                     
7378                     {
7379                         tag: 'label',
7380                         'for' :  id,
7381                         cls : 'control-label col-sm-' + this.labelWidth,
7382                         html : this.fieldLabel
7383                         
7384                     },
7385                     {
7386                         cls : "col-sm-" + (12 - this.labelWidth), 
7387                         cn: [
7388                             combobox
7389                         ]
7390                     }
7391                     
7392                 ];
7393         } else if ( this.fieldLabel.length) {
7394                 Roo.log(" label");
7395                  cfg.cn = [
7396                    
7397                     {
7398                         tag: 'label',
7399                         //cls : 'input-group-addon',
7400                         html : this.fieldLabel
7401                         
7402                     },
7403                     
7404                     combobox
7405                     
7406                 ];
7407
7408         } else {
7409             
7410                 Roo.log(" no label && no align");
7411                 cfg = combobox
7412                      
7413                 
7414         }
7415          
7416         var settings=this;
7417         ['xs','sm','md','lg'].map(function(size){
7418             if (settings[size]) {
7419                 cfg.cls += ' col-' + size + '-' + settings[size];
7420             }
7421         });
7422         
7423         return cfg;
7424         
7425     },
7426     
7427     
7428     
7429     // private
7430     onResize : function(w, h){
7431 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7432 //        if(typeof w == 'number'){
7433 //            var x = w - this.trigger.getWidth();
7434 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7435 //            this.trigger.setStyle('left', x+'px');
7436 //        }
7437     },
7438
7439     // private
7440     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7441
7442     // private
7443     getResizeEl : function(){
7444         return this.inputEl();
7445     },
7446
7447     // private
7448     getPositionEl : function(){
7449         return this.inputEl();
7450     },
7451
7452     // private
7453     alignErrorIcon : function(){
7454         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7455     },
7456
7457     // private
7458     initEvents : function(){
7459         
7460         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7461         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7462         if(!this.multiple){
7463             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7464             if(this.hideTrigger){
7465                 this.trigger.setDisplayed(false);
7466             }
7467             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7468         }
7469         
7470         if(this.multiple){
7471             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7472         }
7473         
7474         //this.trigger.addClassOnOver('x-form-trigger-over');
7475         //this.trigger.addClassOnClick('x-form-trigger-click');
7476         
7477         //if(!this.width){
7478         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7479         //}
7480     },
7481
7482     // private
7483     initTrigger : function(){
7484        
7485     },
7486
7487     // private
7488     onDestroy : function(){
7489         if(this.trigger){
7490             this.trigger.removeAllListeners();
7491           //  this.trigger.remove();
7492         }
7493         //if(this.wrap){
7494         //    this.wrap.remove();
7495         //}
7496         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
7497     },
7498
7499     // private
7500     onFocus : function(){
7501         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
7502         /*
7503         if(!this.mimicing){
7504             this.wrap.addClass('x-trigger-wrap-focus');
7505             this.mimicing = true;
7506             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
7507             if(this.monitorTab){
7508                 this.el.on("keydown", this.checkTab, this);
7509             }
7510         }
7511         */
7512     },
7513
7514     // private
7515     checkTab : function(e){
7516         if(e.getKey() == e.TAB){
7517             this.triggerBlur();
7518         }
7519     },
7520
7521     // private
7522     onBlur : function(){
7523         // do nothing
7524     },
7525
7526     // private
7527     mimicBlur : function(e, t){
7528         /*
7529         if(!this.wrap.contains(t) && this.validateBlur()){
7530             this.triggerBlur();
7531         }
7532         */
7533     },
7534
7535     // private
7536     triggerBlur : function(){
7537         this.mimicing = false;
7538         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7539         if(this.monitorTab){
7540             this.el.un("keydown", this.checkTab, this);
7541         }
7542         //this.wrap.removeClass('x-trigger-wrap-focus');
7543         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7544     },
7545
7546     // private
7547     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7548     validateBlur : function(e, t){
7549         return true;
7550     },
7551
7552     // private
7553     onDisable : function(){
7554         this.inputEl().dom.disabled = true;
7555         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7556         //if(this.wrap){
7557         //    this.wrap.addClass('x-item-disabled');
7558         //}
7559     },
7560
7561     // private
7562     onEnable : function(){
7563         this.inputEl().dom.disabled = false;
7564         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
7565         //if(this.wrap){
7566         //    this.el.removeClass('x-item-disabled');
7567         //}
7568     },
7569
7570     // private
7571     onShow : function(){
7572         var ae = this.getActionEl();
7573         
7574         if(ae){
7575             ae.dom.style.display = '';
7576             ae.dom.style.visibility = 'visible';
7577         }
7578     },
7579
7580     // private
7581     
7582     onHide : function(){
7583         var ae = this.getActionEl();
7584         ae.dom.style.display = 'none';
7585     },
7586
7587     /**
7588      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
7589      * by an implementing function.
7590      * @method
7591      * @param {EventObject} e
7592      */
7593     onTriggerClick : Roo.emptyFn
7594 });
7595  /*
7596  * Based on:
7597  * Ext JS Library 1.1.1
7598  * Copyright(c) 2006-2007, Ext JS, LLC.
7599  *
7600  * Originally Released Under LGPL - original licence link has changed is not relivant.
7601  *
7602  * Fork - LGPL
7603  * <script type="text/javascript">
7604  */
7605
7606
7607 /**
7608  * @class Roo.data.SortTypes
7609  * @singleton
7610  * Defines the default sorting (casting?) comparison functions used when sorting data.
7611  */
7612 Roo.data.SortTypes = {
7613     /**
7614      * Default sort that does nothing
7615      * @param {Mixed} s The value being converted
7616      * @return {Mixed} The comparison value
7617      */
7618     none : function(s){
7619         return s;
7620     },
7621     
7622     /**
7623      * The regular expression used to strip tags
7624      * @type {RegExp}
7625      * @property
7626      */
7627     stripTagsRE : /<\/?[^>]+>/gi,
7628     
7629     /**
7630      * Strips all HTML tags to sort on text only
7631      * @param {Mixed} s The value being converted
7632      * @return {String} The comparison value
7633      */
7634     asText : function(s){
7635         return String(s).replace(this.stripTagsRE, "");
7636     },
7637     
7638     /**
7639      * Strips all HTML tags to sort on text only - Case insensitive
7640      * @param {Mixed} s The value being converted
7641      * @return {String} The comparison value
7642      */
7643     asUCText : function(s){
7644         return String(s).toUpperCase().replace(this.stripTagsRE, "");
7645     },
7646     
7647     /**
7648      * Case insensitive string
7649      * @param {Mixed} s The value being converted
7650      * @return {String} The comparison value
7651      */
7652     asUCString : function(s) {
7653         return String(s).toUpperCase();
7654     },
7655     
7656     /**
7657      * Date sorting
7658      * @param {Mixed} s The value being converted
7659      * @return {Number} The comparison value
7660      */
7661     asDate : function(s) {
7662         if(!s){
7663             return 0;
7664         }
7665         if(s instanceof Date){
7666             return s.getTime();
7667         }
7668         return Date.parse(String(s));
7669     },
7670     
7671     /**
7672      * Float sorting
7673      * @param {Mixed} s The value being converted
7674      * @return {Float} The comparison value
7675      */
7676     asFloat : function(s) {
7677         var val = parseFloat(String(s).replace(/,/g, ""));
7678         if(isNaN(val)) val = 0;
7679         return val;
7680     },
7681     
7682     /**
7683      * Integer sorting
7684      * @param {Mixed} s The value being converted
7685      * @return {Number} The comparison value
7686      */
7687     asInt : function(s) {
7688         var val = parseInt(String(s).replace(/,/g, ""));
7689         if(isNaN(val)) val = 0;
7690         return val;
7691     }
7692 };/*
7693  * Based on:
7694  * Ext JS Library 1.1.1
7695  * Copyright(c) 2006-2007, Ext JS, LLC.
7696  *
7697  * Originally Released Under LGPL - original licence link has changed is not relivant.
7698  *
7699  * Fork - LGPL
7700  * <script type="text/javascript">
7701  */
7702
7703 /**
7704 * @class Roo.data.Record
7705  * Instances of this class encapsulate both record <em>definition</em> information, and record
7706  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
7707  * to access Records cached in an {@link Roo.data.Store} object.<br>
7708  * <p>
7709  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
7710  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
7711  * objects.<br>
7712  * <p>
7713  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
7714  * @constructor
7715  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
7716  * {@link #create}. The parameters are the same.
7717  * @param {Array} data An associative Array of data values keyed by the field name.
7718  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
7719  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
7720  * not specified an integer id is generated.
7721  */
7722 Roo.data.Record = function(data, id){
7723     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
7724     this.data = data;
7725 };
7726
7727 /**
7728  * Generate a constructor for a specific record layout.
7729  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
7730  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
7731  * Each field definition object may contain the following properties: <ul>
7732  * <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,
7733  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
7734  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
7735  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
7736  * is being used, then this is a string containing the javascript expression to reference the data relative to 
7737  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
7738  * to the data item relative to the record element. If the mapping expression is the same as the field name,
7739  * this may be omitted.</p></li>
7740  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
7741  * <ul><li>auto (Default, implies no conversion)</li>
7742  * <li>string</li>
7743  * <li>int</li>
7744  * <li>float</li>
7745  * <li>boolean</li>
7746  * <li>date</li></ul></p></li>
7747  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
7748  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
7749  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
7750  * by the Reader into an object that will be stored in the Record. It is passed the
7751  * following parameters:<ul>
7752  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
7753  * </ul></p></li>
7754  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
7755  * </ul>
7756  * <br>usage:<br><pre><code>
7757 var TopicRecord = Roo.data.Record.create(
7758     {name: 'title', mapping: 'topic_title'},
7759     {name: 'author', mapping: 'username'},
7760     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
7761     {name: 'lastPost', mapping: 'post_time', type: 'date'},
7762     {name: 'lastPoster', mapping: 'user2'},
7763     {name: 'excerpt', mapping: 'post_text'}
7764 );
7765
7766 var myNewRecord = new TopicRecord({
7767     title: 'Do my job please',
7768     author: 'noobie',
7769     totalPosts: 1,
7770     lastPost: new Date(),
7771     lastPoster: 'Animal',
7772     excerpt: 'No way dude!'
7773 });
7774 myStore.add(myNewRecord);
7775 </code></pre>
7776  * @method create
7777  * @static
7778  */
7779 Roo.data.Record.create = function(o){
7780     var f = function(){
7781         f.superclass.constructor.apply(this, arguments);
7782     };
7783     Roo.extend(f, Roo.data.Record);
7784     var p = f.prototype;
7785     p.fields = new Roo.util.MixedCollection(false, function(field){
7786         return field.name;
7787     });
7788     for(var i = 0, len = o.length; i < len; i++){
7789         p.fields.add(new Roo.data.Field(o[i]));
7790     }
7791     f.getField = function(name){
7792         return p.fields.get(name);  
7793     };
7794     return f;
7795 };
7796
7797 Roo.data.Record.AUTO_ID = 1000;
7798 Roo.data.Record.EDIT = 'edit';
7799 Roo.data.Record.REJECT = 'reject';
7800 Roo.data.Record.COMMIT = 'commit';
7801
7802 Roo.data.Record.prototype = {
7803     /**
7804      * Readonly flag - true if this record has been modified.
7805      * @type Boolean
7806      */
7807     dirty : false,
7808     editing : false,
7809     error: null,
7810     modified: null,
7811
7812     // private
7813     join : function(store){
7814         this.store = store;
7815     },
7816
7817     /**
7818      * Set the named field to the specified value.
7819      * @param {String} name The name of the field to set.
7820      * @param {Object} value The value to set the field to.
7821      */
7822     set : function(name, value){
7823         if(this.data[name] == value){
7824             return;
7825         }
7826         this.dirty = true;
7827         if(!this.modified){
7828             this.modified = {};
7829         }
7830         if(typeof this.modified[name] == 'undefined'){
7831             this.modified[name] = this.data[name];
7832         }
7833         this.data[name] = value;
7834         if(!this.editing && this.store){
7835             this.store.afterEdit(this);
7836         }       
7837     },
7838
7839     /**
7840      * Get the value of the named field.
7841      * @param {String} name The name of the field to get the value of.
7842      * @return {Object} The value of the field.
7843      */
7844     get : function(name){
7845         return this.data[name]; 
7846     },
7847
7848     // private
7849     beginEdit : function(){
7850         this.editing = true;
7851         this.modified = {}; 
7852     },
7853
7854     // private
7855     cancelEdit : function(){
7856         this.editing = false;
7857         delete this.modified;
7858     },
7859
7860     // private
7861     endEdit : function(){
7862         this.editing = false;
7863         if(this.dirty && this.store){
7864             this.store.afterEdit(this);
7865         }
7866     },
7867
7868     /**
7869      * Usually called by the {@link Roo.data.Store} which owns the Record.
7870      * Rejects all changes made to the Record since either creation, or the last commit operation.
7871      * Modified fields are reverted to their original values.
7872      * <p>
7873      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7874      * of reject operations.
7875      */
7876     reject : function(){
7877         var m = this.modified;
7878         for(var n in m){
7879             if(typeof m[n] != "function"){
7880                 this.data[n] = m[n];
7881             }
7882         }
7883         this.dirty = false;
7884         delete this.modified;
7885         this.editing = false;
7886         if(this.store){
7887             this.store.afterReject(this);
7888         }
7889     },
7890
7891     /**
7892      * Usually called by the {@link Roo.data.Store} which owns the Record.
7893      * Commits all changes made to the Record since either creation, or the last commit operation.
7894      * <p>
7895      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7896      * of commit operations.
7897      */
7898     commit : function(){
7899         this.dirty = false;
7900         delete this.modified;
7901         this.editing = false;
7902         if(this.store){
7903             this.store.afterCommit(this);
7904         }
7905     },
7906
7907     // private
7908     hasError : function(){
7909         return this.error != null;
7910     },
7911
7912     // private
7913     clearError : function(){
7914         this.error = null;
7915     },
7916
7917     /**
7918      * Creates a copy of this record.
7919      * @param {String} id (optional) A new record id if you don't want to use this record's id
7920      * @return {Record}
7921      */
7922     copy : function(newId) {
7923         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
7924     }
7925 };/*
7926  * Based on:
7927  * Ext JS Library 1.1.1
7928  * Copyright(c) 2006-2007, Ext JS, LLC.
7929  *
7930  * Originally Released Under LGPL - original licence link has changed is not relivant.
7931  *
7932  * Fork - LGPL
7933  * <script type="text/javascript">
7934  */
7935
7936
7937
7938 /**
7939  * @class Roo.data.Store
7940  * @extends Roo.util.Observable
7941  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
7942  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
7943  * <p>
7944  * 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
7945  * has no knowledge of the format of the data returned by the Proxy.<br>
7946  * <p>
7947  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
7948  * instances from the data object. These records are cached and made available through accessor functions.
7949  * @constructor
7950  * Creates a new Store.
7951  * @param {Object} config A config object containing the objects needed for the Store to access data,
7952  * and read the data into Records.
7953  */
7954 Roo.data.Store = function(config){
7955     this.data = new Roo.util.MixedCollection(false);
7956     this.data.getKey = function(o){
7957         return o.id;
7958     };
7959     this.baseParams = {};
7960     // private
7961     this.paramNames = {
7962         "start" : "start",
7963         "limit" : "limit",
7964         "sort" : "sort",
7965         "dir" : "dir",
7966         "multisort" : "_multisort"
7967     };
7968
7969     if(config && config.data){
7970         this.inlineData = config.data;
7971         delete config.data;
7972     }
7973
7974     Roo.apply(this, config);
7975     
7976     if(this.reader){ // reader passed
7977         this.reader = Roo.factory(this.reader, Roo.data);
7978         this.reader.xmodule = this.xmodule || false;
7979         if(!this.recordType){
7980             this.recordType = this.reader.recordType;
7981         }
7982         if(this.reader.onMetaChange){
7983             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
7984         }
7985     }
7986
7987     if(this.recordType){
7988         this.fields = this.recordType.prototype.fields;
7989     }
7990     this.modified = [];
7991
7992     this.addEvents({
7993         /**
7994          * @event datachanged
7995          * Fires when the data cache has changed, and a widget which is using this Store
7996          * as a Record cache should refresh its view.
7997          * @param {Store} this
7998          */
7999         datachanged : true,
8000         /**
8001          * @event metachange
8002          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8003          * @param {Store} this
8004          * @param {Object} meta The JSON metadata
8005          */
8006         metachange : true,
8007         /**
8008          * @event add
8009          * Fires when Records have been added to the Store
8010          * @param {Store} this
8011          * @param {Roo.data.Record[]} records The array of Records added
8012          * @param {Number} index The index at which the record(s) were added
8013          */
8014         add : true,
8015         /**
8016          * @event remove
8017          * Fires when a Record has been removed from the Store
8018          * @param {Store} this
8019          * @param {Roo.data.Record} record The Record that was removed
8020          * @param {Number} index The index at which the record was removed
8021          */
8022         remove : true,
8023         /**
8024          * @event update
8025          * Fires when a Record has been updated
8026          * @param {Store} this
8027          * @param {Roo.data.Record} record The Record that was updated
8028          * @param {String} operation The update operation being performed.  Value may be one of:
8029          * <pre><code>
8030  Roo.data.Record.EDIT
8031  Roo.data.Record.REJECT
8032  Roo.data.Record.COMMIT
8033          * </code></pre>
8034          */
8035         update : true,
8036         /**
8037          * @event clear
8038          * Fires when the data cache has been cleared.
8039          * @param {Store} this
8040          */
8041         clear : true,
8042         /**
8043          * @event beforeload
8044          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8045          * the load action will be canceled.
8046          * @param {Store} this
8047          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8048          */
8049         beforeload : true,
8050         /**
8051          * @event beforeloadadd
8052          * Fires after a new set of Records has been loaded.
8053          * @param {Store} this
8054          * @param {Roo.data.Record[]} records The Records that were loaded
8055          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8056          */
8057         beforeloadadd : true,
8058         /**
8059          * @event load
8060          * Fires after a new set of Records has been loaded, before they are added to the store.
8061          * @param {Store} this
8062          * @param {Roo.data.Record[]} records The Records that were loaded
8063          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8064          * @params {Object} return from reader
8065          */
8066         load : true,
8067         /**
8068          * @event loadexception
8069          * Fires if an exception occurs in the Proxy during loading.
8070          * Called with the signature of the Proxy's "loadexception" event.
8071          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8072          * 
8073          * @param {Proxy} 
8074          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8075          * @param {Object} load options 
8076          * @param {Object} jsonData from your request (normally this contains the Exception)
8077          */
8078         loadexception : true
8079     });
8080     
8081     if(this.proxy){
8082         this.proxy = Roo.factory(this.proxy, Roo.data);
8083         this.proxy.xmodule = this.xmodule || false;
8084         this.relayEvents(this.proxy,  ["loadexception"]);
8085     }
8086     this.sortToggle = {};
8087     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8088
8089     Roo.data.Store.superclass.constructor.call(this);
8090
8091     if(this.inlineData){
8092         this.loadData(this.inlineData);
8093         delete this.inlineData;
8094     }
8095 };
8096
8097 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8098      /**
8099     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8100     * without a remote query - used by combo/forms at present.
8101     */
8102     
8103     /**
8104     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8105     */
8106     /**
8107     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8108     */
8109     /**
8110     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8111     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8112     */
8113     /**
8114     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8115     * on any HTTP request
8116     */
8117     /**
8118     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8119     */
8120     /**
8121     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8122     */
8123     multiSort: false,
8124     /**
8125     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8126     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8127     */
8128     remoteSort : false,
8129
8130     /**
8131     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8132      * loaded or when a record is removed. (defaults to false).
8133     */
8134     pruneModifiedRecords : false,
8135
8136     // private
8137     lastOptions : null,
8138
8139     /**
8140      * Add Records to the Store and fires the add event.
8141      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8142      */
8143     add : function(records){
8144         records = [].concat(records);
8145         for(var i = 0, len = records.length; i < len; i++){
8146             records[i].join(this);
8147         }
8148         var index = this.data.length;
8149         this.data.addAll(records);
8150         this.fireEvent("add", this, records, index);
8151     },
8152
8153     /**
8154      * Remove a Record from the Store and fires the remove event.
8155      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8156      */
8157     remove : function(record){
8158         var index = this.data.indexOf(record);
8159         this.data.removeAt(index);
8160         if(this.pruneModifiedRecords){
8161             this.modified.remove(record);
8162         }
8163         this.fireEvent("remove", this, record, index);
8164     },
8165
8166     /**
8167      * Remove all Records from the Store and fires the clear event.
8168      */
8169     removeAll : function(){
8170         this.data.clear();
8171         if(this.pruneModifiedRecords){
8172             this.modified = [];
8173         }
8174         this.fireEvent("clear", this);
8175     },
8176
8177     /**
8178      * Inserts Records to the Store at the given index and fires the add event.
8179      * @param {Number} index The start index at which to insert the passed Records.
8180      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8181      */
8182     insert : function(index, records){
8183         records = [].concat(records);
8184         for(var i = 0, len = records.length; i < len; i++){
8185             this.data.insert(index, records[i]);
8186             records[i].join(this);
8187         }
8188         this.fireEvent("add", this, records, index);
8189     },
8190
8191     /**
8192      * Get the index within the cache of the passed Record.
8193      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8194      * @return {Number} The index of the passed Record. Returns -1 if not found.
8195      */
8196     indexOf : function(record){
8197         return this.data.indexOf(record);
8198     },
8199
8200     /**
8201      * Get the index within the cache of the Record with the passed id.
8202      * @param {String} id The id of the Record to find.
8203      * @return {Number} The index of the Record. Returns -1 if not found.
8204      */
8205     indexOfId : function(id){
8206         return this.data.indexOfKey(id);
8207     },
8208
8209     /**
8210      * Get the Record with the specified id.
8211      * @param {String} id The id of the Record to find.
8212      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8213      */
8214     getById : function(id){
8215         return this.data.key(id);
8216     },
8217
8218     /**
8219      * Get the Record at the specified index.
8220      * @param {Number} index The index of the Record to find.
8221      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8222      */
8223     getAt : function(index){
8224         return this.data.itemAt(index);
8225     },
8226
8227     /**
8228      * Returns a range of Records between specified indices.
8229      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8230      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8231      * @return {Roo.data.Record[]} An array of Records
8232      */
8233     getRange : function(start, end){
8234         return this.data.getRange(start, end);
8235     },
8236
8237     // private
8238     storeOptions : function(o){
8239         o = Roo.apply({}, o);
8240         delete o.callback;
8241         delete o.scope;
8242         this.lastOptions = o;
8243     },
8244
8245     /**
8246      * Loads the Record cache from the configured Proxy using the configured Reader.
8247      * <p>
8248      * If using remote paging, then the first load call must specify the <em>start</em>
8249      * and <em>limit</em> properties in the options.params property to establish the initial
8250      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8251      * <p>
8252      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8253      * and this call will return before the new data has been loaded. Perform any post-processing
8254      * in a callback function, or in a "load" event handler.</strong>
8255      * <p>
8256      * @param {Object} options An object containing properties which control loading options:<ul>
8257      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8258      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8259      * passed the following arguments:<ul>
8260      * <li>r : Roo.data.Record[]</li>
8261      * <li>options: Options object from the load call</li>
8262      * <li>success: Boolean success indicator</li></ul></li>
8263      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8264      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8265      * </ul>
8266      */
8267     load : function(options){
8268         options = options || {};
8269         if(this.fireEvent("beforeload", this, options) !== false){
8270             this.storeOptions(options);
8271             var p = Roo.apply(options.params || {}, this.baseParams);
8272             // if meta was not loaded from remote source.. try requesting it.
8273             if (!this.reader.metaFromRemote) {
8274                 p._requestMeta = 1;
8275             }
8276             if(this.sortInfo && this.remoteSort){
8277                 var pn = this.paramNames;
8278                 p[pn["sort"]] = this.sortInfo.field;
8279                 p[pn["dir"]] = this.sortInfo.direction;
8280             }
8281             if (this.multiSort) {
8282                 var pn = this.paramNames;
8283                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8284             }
8285             
8286             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8287         }
8288     },
8289
8290     /**
8291      * Reloads the Record cache from the configured Proxy using the configured Reader and
8292      * the options from the last load operation performed.
8293      * @param {Object} options (optional) An object containing properties which may override the options
8294      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8295      * the most recently used options are reused).
8296      */
8297     reload : function(options){
8298         this.load(Roo.applyIf(options||{}, this.lastOptions));
8299     },
8300
8301     // private
8302     // Called as a callback by the Reader during a load operation.
8303     loadRecords : function(o, options, success){
8304         if(!o || success === false){
8305             if(success !== false){
8306                 this.fireEvent("load", this, [], options, o);
8307             }
8308             if(options.callback){
8309                 options.callback.call(options.scope || this, [], options, false);
8310             }
8311             return;
8312         }
8313         // if data returned failure - throw an exception.
8314         if (o.success === false) {
8315             // show a message if no listener is registered.
8316             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8317                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8318             }
8319             // loadmask wil be hooked into this..
8320             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8321             return;
8322         }
8323         var r = o.records, t = o.totalRecords || r.length;
8324         
8325         this.fireEvent("beforeloadadd", this, r, options, o);
8326         
8327         if(!options || options.add !== true){
8328             if(this.pruneModifiedRecords){
8329                 this.modified = [];
8330             }
8331             for(var i = 0, len = r.length; i < len; i++){
8332                 r[i].join(this);
8333             }
8334             if(this.snapshot){
8335                 this.data = this.snapshot;
8336                 delete this.snapshot;
8337             }
8338             this.data.clear();
8339             this.data.addAll(r);
8340             this.totalLength = t;
8341             this.applySort();
8342             this.fireEvent("datachanged", this);
8343         }else{
8344             this.totalLength = Math.max(t, this.data.length+r.length);
8345             this.add(r);
8346         }
8347         this.fireEvent("load", this, r, options, o);
8348         if(options.callback){
8349             options.callback.call(options.scope || this, r, options, true);
8350         }
8351     },
8352
8353
8354     /**
8355      * Loads data from a passed data block. A Reader which understands the format of the data
8356      * must have been configured in the constructor.
8357      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8358      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8359      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8360      */
8361     loadData : function(o, append){
8362         var r = this.reader.readRecords(o);
8363         this.loadRecords(r, {add: append}, true);
8364     },
8365
8366     /**
8367      * Gets the number of cached records.
8368      * <p>
8369      * <em>If using paging, this may not be the total size of the dataset. If the data object
8370      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8371      * the data set size</em>
8372      */
8373     getCount : function(){
8374         return this.data.length || 0;
8375     },
8376
8377     /**
8378      * Gets the total number of records in the dataset as returned by the server.
8379      * <p>
8380      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8381      * the dataset size</em>
8382      */
8383     getTotalCount : function(){
8384         return this.totalLength || 0;
8385     },
8386
8387     /**
8388      * Returns the sort state of the Store as an object with two properties:
8389      * <pre><code>
8390  field {String} The name of the field by which the Records are sorted
8391  direction {String} The sort order, "ASC" or "DESC"
8392      * </code></pre>
8393      */
8394     getSortState : function(){
8395         return this.sortInfo;
8396     },
8397
8398     // private
8399     applySort : function(){
8400         if(this.sortInfo && !this.remoteSort){
8401             var s = this.sortInfo, f = s.field;
8402             var st = this.fields.get(f).sortType;
8403             var fn = function(r1, r2){
8404                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8405                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8406             };
8407             this.data.sort(s.direction, fn);
8408             if(this.snapshot && this.snapshot != this.data){
8409                 this.snapshot.sort(s.direction, fn);
8410             }
8411         }
8412     },
8413
8414     /**
8415      * Sets the default sort column and order to be used by the next load operation.
8416      * @param {String} fieldName The name of the field to sort by.
8417      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8418      */
8419     setDefaultSort : function(field, dir){
8420         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8421     },
8422
8423     /**
8424      * Sort the Records.
8425      * If remote sorting is used, the sort is performed on the server, and the cache is
8426      * reloaded. If local sorting is used, the cache is sorted internally.
8427      * @param {String} fieldName The name of the field to sort by.
8428      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8429      */
8430     sort : function(fieldName, dir){
8431         var f = this.fields.get(fieldName);
8432         if(!dir){
8433             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8434             
8435             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8436                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8437             }else{
8438                 dir = f.sortDir;
8439             }
8440         }
8441         this.sortToggle[f.name] = dir;
8442         this.sortInfo = {field: f.name, direction: dir};
8443         if(!this.remoteSort){
8444             this.applySort();
8445             this.fireEvent("datachanged", this);
8446         }else{
8447             this.load(this.lastOptions);
8448         }
8449     },
8450
8451     /**
8452      * Calls the specified function for each of the Records in the cache.
8453      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8454      * Returning <em>false</em> aborts and exits the iteration.
8455      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8456      */
8457     each : function(fn, scope){
8458         this.data.each(fn, scope);
8459     },
8460
8461     /**
8462      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8463      * (e.g., during paging).
8464      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8465      */
8466     getModifiedRecords : function(){
8467         return this.modified;
8468     },
8469
8470     // private
8471     createFilterFn : function(property, value, anyMatch){
8472         if(!value.exec){ // not a regex
8473             value = String(value);
8474             if(value.length == 0){
8475                 return false;
8476             }
8477             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8478         }
8479         return function(r){
8480             return value.test(r.data[property]);
8481         };
8482     },
8483
8484     /**
8485      * Sums the value of <i>property</i> for each record between start and end and returns the result.
8486      * @param {String} property A field on your records
8487      * @param {Number} start The record index to start at (defaults to 0)
8488      * @param {Number} end The last record index to include (defaults to length - 1)
8489      * @return {Number} The sum
8490      */
8491     sum : function(property, start, end){
8492         var rs = this.data.items, v = 0;
8493         start = start || 0;
8494         end = (end || end === 0) ? end : rs.length-1;
8495
8496         for(var i = start; i <= end; i++){
8497             v += (rs[i].data[property] || 0);
8498         }
8499         return v;
8500     },
8501
8502     /**
8503      * Filter the records by a specified property.
8504      * @param {String} field A field on your records
8505      * @param {String/RegExp} value Either a string that the field
8506      * should start with or a RegExp to test against the field
8507      * @param {Boolean} anyMatch True to match any part not just the beginning
8508      */
8509     filter : function(property, value, anyMatch){
8510         var fn = this.createFilterFn(property, value, anyMatch);
8511         return fn ? this.filterBy(fn) : this.clearFilter();
8512     },
8513
8514     /**
8515      * Filter by a function. The specified function will be called with each
8516      * record in this data source. If the function returns true the record is included,
8517      * otherwise it is filtered.
8518      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8519      * @param {Object} scope (optional) The scope of the function (defaults to this)
8520      */
8521     filterBy : function(fn, scope){
8522         this.snapshot = this.snapshot || this.data;
8523         this.data = this.queryBy(fn, scope||this);
8524         this.fireEvent("datachanged", this);
8525     },
8526
8527     /**
8528      * Query the records by a specified property.
8529      * @param {String} field A field on your records
8530      * @param {String/RegExp} value Either a string that the field
8531      * should start with or a RegExp to test against the field
8532      * @param {Boolean} anyMatch True to match any part not just the beginning
8533      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8534      */
8535     query : function(property, value, anyMatch){
8536         var fn = this.createFilterFn(property, value, anyMatch);
8537         return fn ? this.queryBy(fn) : this.data.clone();
8538     },
8539
8540     /**
8541      * Query by a function. The specified function will be called with each
8542      * record in this data source. If the function returns true the record is included
8543      * in the results.
8544      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8545      * @param {Object} scope (optional) The scope of the function (defaults to this)
8546       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8547      **/
8548     queryBy : function(fn, scope){
8549         var data = this.snapshot || this.data;
8550         return data.filterBy(fn, scope||this);
8551     },
8552
8553     /**
8554      * Collects unique values for a particular dataIndex from this store.
8555      * @param {String} dataIndex The property to collect
8556      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8557      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8558      * @return {Array} An array of the unique values
8559      **/
8560     collect : function(dataIndex, allowNull, bypassFilter){
8561         var d = (bypassFilter === true && this.snapshot) ?
8562                 this.snapshot.items : this.data.items;
8563         var v, sv, r = [], l = {};
8564         for(var i = 0, len = d.length; i < len; i++){
8565             v = d[i].data[dataIndex];
8566             sv = String(v);
8567             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
8568                 l[sv] = true;
8569                 r[r.length] = v;
8570             }
8571         }
8572         return r;
8573     },
8574
8575     /**
8576      * Revert to a view of the Record cache with no filtering applied.
8577      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
8578      */
8579     clearFilter : function(suppressEvent){
8580         if(this.snapshot && this.snapshot != this.data){
8581             this.data = this.snapshot;
8582             delete this.snapshot;
8583             if(suppressEvent !== true){
8584                 this.fireEvent("datachanged", this);
8585             }
8586         }
8587     },
8588
8589     // private
8590     afterEdit : function(record){
8591         if(this.modified.indexOf(record) == -1){
8592             this.modified.push(record);
8593         }
8594         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
8595     },
8596     
8597     // private
8598     afterReject : function(record){
8599         this.modified.remove(record);
8600         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
8601     },
8602
8603     // private
8604     afterCommit : function(record){
8605         this.modified.remove(record);
8606         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
8607     },
8608
8609     /**
8610      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
8611      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
8612      */
8613     commitChanges : function(){
8614         var m = this.modified.slice(0);
8615         this.modified = [];
8616         for(var i = 0, len = m.length; i < len; i++){
8617             m[i].commit();
8618         }
8619     },
8620
8621     /**
8622      * Cancel outstanding changes on all changed records.
8623      */
8624     rejectChanges : function(){
8625         var m = this.modified.slice(0);
8626         this.modified = [];
8627         for(var i = 0, len = m.length; i < len; i++){
8628             m[i].reject();
8629         }
8630     },
8631
8632     onMetaChange : function(meta, rtype, o){
8633         this.recordType = rtype;
8634         this.fields = rtype.prototype.fields;
8635         delete this.snapshot;
8636         this.sortInfo = meta.sortInfo || this.sortInfo;
8637         this.modified = [];
8638         this.fireEvent('metachange', this, this.reader.meta);
8639     },
8640     
8641     moveIndex : function(data, type)
8642     {
8643         var index = this.indexOf(data);
8644         
8645         var newIndex = index + type;
8646         
8647         this.remove(data);
8648         
8649         this.insert(newIndex, data);
8650         
8651     }
8652 });/*
8653  * Based on:
8654  * Ext JS Library 1.1.1
8655  * Copyright(c) 2006-2007, Ext JS, LLC.
8656  *
8657  * Originally Released Under LGPL - original licence link has changed is not relivant.
8658  *
8659  * Fork - LGPL
8660  * <script type="text/javascript">
8661  */
8662
8663 /**
8664  * @class Roo.data.SimpleStore
8665  * @extends Roo.data.Store
8666  * Small helper class to make creating Stores from Array data easier.
8667  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
8668  * @cfg {Array} fields An array of field definition objects, or field name strings.
8669  * @cfg {Array} data The multi-dimensional array of data
8670  * @constructor
8671  * @param {Object} config
8672  */
8673 Roo.data.SimpleStore = function(config){
8674     Roo.data.SimpleStore.superclass.constructor.call(this, {
8675         isLocal : true,
8676         reader: new Roo.data.ArrayReader({
8677                 id: config.id
8678             },
8679             Roo.data.Record.create(config.fields)
8680         ),
8681         proxy : new Roo.data.MemoryProxy(config.data)
8682     });
8683     this.load();
8684 };
8685 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
8686  * Based on:
8687  * Ext JS Library 1.1.1
8688  * Copyright(c) 2006-2007, Ext JS, LLC.
8689  *
8690  * Originally Released Under LGPL - original licence link has changed is not relivant.
8691  *
8692  * Fork - LGPL
8693  * <script type="text/javascript">
8694  */
8695
8696 /**
8697 /**
8698  * @extends Roo.data.Store
8699  * @class Roo.data.JsonStore
8700  * Small helper class to make creating Stores for JSON data easier. <br/>
8701 <pre><code>
8702 var store = new Roo.data.JsonStore({
8703     url: 'get-images.php',
8704     root: 'images',
8705     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
8706 });
8707 </code></pre>
8708  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
8709  * JsonReader and HttpProxy (unless inline data is provided).</b>
8710  * @cfg {Array} fields An array of field definition objects, or field name strings.
8711  * @constructor
8712  * @param {Object} config
8713  */
8714 Roo.data.JsonStore = function(c){
8715     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
8716         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
8717         reader: new Roo.data.JsonReader(c, c.fields)
8718     }));
8719 };
8720 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
8721  * Based on:
8722  * Ext JS Library 1.1.1
8723  * Copyright(c) 2006-2007, Ext JS, LLC.
8724  *
8725  * Originally Released Under LGPL - original licence link has changed is not relivant.
8726  *
8727  * Fork - LGPL
8728  * <script type="text/javascript">
8729  */
8730
8731  
8732 Roo.data.Field = function(config){
8733     if(typeof config == "string"){
8734         config = {name: config};
8735     }
8736     Roo.apply(this, config);
8737     
8738     if(!this.type){
8739         this.type = "auto";
8740     }
8741     
8742     var st = Roo.data.SortTypes;
8743     // named sortTypes are supported, here we look them up
8744     if(typeof this.sortType == "string"){
8745         this.sortType = st[this.sortType];
8746     }
8747     
8748     // set default sortType for strings and dates
8749     if(!this.sortType){
8750         switch(this.type){
8751             case "string":
8752                 this.sortType = st.asUCString;
8753                 break;
8754             case "date":
8755                 this.sortType = st.asDate;
8756                 break;
8757             default:
8758                 this.sortType = st.none;
8759         }
8760     }
8761
8762     // define once
8763     var stripRe = /[\$,%]/g;
8764
8765     // prebuilt conversion function for this field, instead of
8766     // switching every time we're reading a value
8767     if(!this.convert){
8768         var cv, dateFormat = this.dateFormat;
8769         switch(this.type){
8770             case "":
8771             case "auto":
8772             case undefined:
8773                 cv = function(v){ return v; };
8774                 break;
8775             case "string":
8776                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
8777                 break;
8778             case "int":
8779                 cv = function(v){
8780                     return v !== undefined && v !== null && v !== '' ?
8781                            parseInt(String(v).replace(stripRe, ""), 10) : '';
8782                     };
8783                 break;
8784             case "float":
8785                 cv = function(v){
8786                     return v !== undefined && v !== null && v !== '' ?
8787                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
8788                     };
8789                 break;
8790             case "bool":
8791             case "boolean":
8792                 cv = function(v){ return v === true || v === "true" || v == 1; };
8793                 break;
8794             case "date":
8795                 cv = function(v){
8796                     if(!v){
8797                         return '';
8798                     }
8799                     if(v instanceof Date){
8800                         return v;
8801                     }
8802                     if(dateFormat){
8803                         if(dateFormat == "timestamp"){
8804                             return new Date(v*1000);
8805                         }
8806                         return Date.parseDate(v, dateFormat);
8807                     }
8808                     var parsed = Date.parse(v);
8809                     return parsed ? new Date(parsed) : null;
8810                 };
8811              break;
8812             
8813         }
8814         this.convert = cv;
8815     }
8816 };
8817
8818 Roo.data.Field.prototype = {
8819     dateFormat: null,
8820     defaultValue: "",
8821     mapping: null,
8822     sortType : null,
8823     sortDir : "ASC"
8824 };/*
8825  * Based on:
8826  * Ext JS Library 1.1.1
8827  * Copyright(c) 2006-2007, Ext JS, LLC.
8828  *
8829  * Originally Released Under LGPL - original licence link has changed is not relivant.
8830  *
8831  * Fork - LGPL
8832  * <script type="text/javascript">
8833  */
8834  
8835 // Base class for reading structured data from a data source.  This class is intended to be
8836 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
8837
8838 /**
8839  * @class Roo.data.DataReader
8840  * Base class for reading structured data from a data source.  This class is intended to be
8841  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
8842  */
8843
8844 Roo.data.DataReader = function(meta, recordType){
8845     
8846     this.meta = meta;
8847     
8848     this.recordType = recordType instanceof Array ? 
8849         Roo.data.Record.create(recordType) : recordType;
8850 };
8851
8852 Roo.data.DataReader.prototype = {
8853      /**
8854      * Create an empty record
8855      * @param {Object} data (optional) - overlay some values
8856      * @return {Roo.data.Record} record created.
8857      */
8858     newRow :  function(d) {
8859         var da =  {};
8860         this.recordType.prototype.fields.each(function(c) {
8861             switch( c.type) {
8862                 case 'int' : da[c.name] = 0; break;
8863                 case 'date' : da[c.name] = new Date(); break;
8864                 case 'float' : da[c.name] = 0.0; break;
8865                 case 'boolean' : da[c.name] = false; break;
8866                 default : da[c.name] = ""; break;
8867             }
8868             
8869         });
8870         return new this.recordType(Roo.apply(da, d));
8871     }
8872     
8873 };/*
8874  * Based on:
8875  * Ext JS Library 1.1.1
8876  * Copyright(c) 2006-2007, Ext JS, LLC.
8877  *
8878  * Originally Released Under LGPL - original licence link has changed is not relivant.
8879  *
8880  * Fork - LGPL
8881  * <script type="text/javascript">
8882  */
8883
8884 /**
8885  * @class Roo.data.DataProxy
8886  * @extends Roo.data.Observable
8887  * This class is an abstract base class for implementations which provide retrieval of
8888  * unformatted data objects.<br>
8889  * <p>
8890  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
8891  * (of the appropriate type which knows how to parse the data object) to provide a block of
8892  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
8893  * <p>
8894  * Custom implementations must implement the load method as described in
8895  * {@link Roo.data.HttpProxy#load}.
8896  */
8897 Roo.data.DataProxy = function(){
8898     this.addEvents({
8899         /**
8900          * @event beforeload
8901          * Fires before a network request is made to retrieve a data object.
8902          * @param {Object} This DataProxy object.
8903          * @param {Object} params The params parameter to the load function.
8904          */
8905         beforeload : true,
8906         /**
8907          * @event load
8908          * Fires before the load method's callback is called.
8909          * @param {Object} This DataProxy object.
8910          * @param {Object} o The data object.
8911          * @param {Object} arg The callback argument object passed to the load function.
8912          */
8913         load : true,
8914         /**
8915          * @event loadexception
8916          * Fires if an Exception occurs during data retrieval.
8917          * @param {Object} This DataProxy object.
8918          * @param {Object} o The data object.
8919          * @param {Object} arg The callback argument object passed to the load function.
8920          * @param {Object} e The Exception.
8921          */
8922         loadexception : true
8923     });
8924     Roo.data.DataProxy.superclass.constructor.call(this);
8925 };
8926
8927 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
8928
8929     /**
8930      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
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  * @class Roo.data.MemoryProxy
8944  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
8945  * to the Reader when its load method is called.
8946  * @constructor
8947  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
8948  */
8949 Roo.data.MemoryProxy = function(data){
8950     if (data.data) {
8951         data = data.data;
8952     }
8953     Roo.data.MemoryProxy.superclass.constructor.call(this);
8954     this.data = data;
8955 };
8956
8957 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
8958     /**
8959      * Load data from the requested source (in this case an in-memory
8960      * data object passed to the constructor), read the data object into
8961      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8962      * process that block using the passed callback.
8963      * @param {Object} params This parameter is not used by the MemoryProxy class.
8964      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8965      * object into a block of Roo.data.Records.
8966      * @param {Function} callback The function into which to pass the block of Roo.data.records.
8967      * The function must be passed <ul>
8968      * <li>The Record block object</li>
8969      * <li>The "arg" argument from the load function</li>
8970      * <li>A boolean success indicator</li>
8971      * </ul>
8972      * @param {Object} scope The scope in which to call the callback
8973      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8974      */
8975     load : function(params, reader, callback, scope, arg){
8976         params = params || {};
8977         var result;
8978         try {
8979             result = reader.readRecords(this.data);
8980         }catch(e){
8981             this.fireEvent("loadexception", this, arg, null, e);
8982             callback.call(scope, null, arg, false);
8983             return;
8984         }
8985         callback.call(scope, result, arg, true);
8986     },
8987     
8988     // private
8989     update : function(params, records){
8990         
8991     }
8992 });/*
8993  * Based on:
8994  * Ext JS Library 1.1.1
8995  * Copyright(c) 2006-2007, Ext JS, LLC.
8996  *
8997  * Originally Released Under LGPL - original licence link has changed is not relivant.
8998  *
8999  * Fork - LGPL
9000  * <script type="text/javascript">
9001  */
9002 /**
9003  * @class Roo.data.HttpProxy
9004  * @extends Roo.data.DataProxy
9005  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9006  * configured to reference a certain URL.<br><br>
9007  * <p>
9008  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9009  * from which the running page was served.<br><br>
9010  * <p>
9011  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9012  * <p>
9013  * Be aware that to enable the browser to parse an XML document, the server must set
9014  * the Content-Type header in the HTTP response to "text/xml".
9015  * @constructor
9016  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9017  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9018  * will be used to make the request.
9019  */
9020 Roo.data.HttpProxy = function(conn){
9021     Roo.data.HttpProxy.superclass.constructor.call(this);
9022     // is conn a conn config or a real conn?
9023     this.conn = conn;
9024     this.useAjax = !conn || !conn.events;
9025   
9026 };
9027
9028 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9029     // thse are take from connection...
9030     
9031     /**
9032      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9033      */
9034     /**
9035      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9036      * extra parameters to each request made by this object. (defaults to undefined)
9037      */
9038     /**
9039      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9040      *  to each request made by this object. (defaults to undefined)
9041      */
9042     /**
9043      * @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)
9044      */
9045     /**
9046      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9047      */
9048      /**
9049      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9050      * @type Boolean
9051      */
9052   
9053
9054     /**
9055      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9056      * @type Boolean
9057      */
9058     /**
9059      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9060      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9061      * a finer-grained basis than the DataProxy events.
9062      */
9063     getConnection : function(){
9064         return this.useAjax ? Roo.Ajax : this.conn;
9065     },
9066
9067     /**
9068      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9069      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9070      * process that block using the passed callback.
9071      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9072      * for the request to the remote server.
9073      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9074      * object into a block of Roo.data.Records.
9075      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9076      * The function must be passed <ul>
9077      * <li>The Record block object</li>
9078      * <li>The "arg" argument from the load function</li>
9079      * <li>A boolean success indicator</li>
9080      * </ul>
9081      * @param {Object} scope The scope in which to call the callback
9082      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9083      */
9084     load : function(params, reader, callback, scope, arg){
9085         if(this.fireEvent("beforeload", this, params) !== false){
9086             var  o = {
9087                 params : params || {},
9088                 request: {
9089                     callback : callback,
9090                     scope : scope,
9091                     arg : arg
9092                 },
9093                 reader: reader,
9094                 callback : this.loadResponse,
9095                 scope: this
9096             };
9097             if(this.useAjax){
9098                 Roo.applyIf(o, this.conn);
9099                 if(this.activeRequest){
9100                     Roo.Ajax.abort(this.activeRequest);
9101                 }
9102                 this.activeRequest = Roo.Ajax.request(o);
9103             }else{
9104                 this.conn.request(o);
9105             }
9106         }else{
9107             callback.call(scope||this, null, arg, false);
9108         }
9109     },
9110
9111     // private
9112     loadResponse : function(o, success, response){
9113         delete this.activeRequest;
9114         if(!success){
9115             this.fireEvent("loadexception", this, o, response);
9116             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9117             return;
9118         }
9119         var result;
9120         try {
9121             result = o.reader.read(response);
9122         }catch(e){
9123             this.fireEvent("loadexception", this, o, response, e);
9124             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9125             return;
9126         }
9127         
9128         this.fireEvent("load", this, o, o.request.arg);
9129         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9130     },
9131
9132     // private
9133     update : function(dataSet){
9134
9135     },
9136
9137     // private
9138     updateResponse : function(dataSet){
9139
9140     }
9141 });/*
9142  * Based on:
9143  * Ext JS Library 1.1.1
9144  * Copyright(c) 2006-2007, Ext JS, LLC.
9145  *
9146  * Originally Released Under LGPL - original licence link has changed is not relivant.
9147  *
9148  * Fork - LGPL
9149  * <script type="text/javascript">
9150  */
9151
9152 /**
9153  * @class Roo.data.ScriptTagProxy
9154  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9155  * other than the originating domain of the running page.<br><br>
9156  * <p>
9157  * <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
9158  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9159  * <p>
9160  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9161  * source code that is used as the source inside a &lt;script> tag.<br><br>
9162  * <p>
9163  * In order for the browser to process the returned data, the server must wrap the data object
9164  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9165  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9166  * depending on whether the callback name was passed:
9167  * <p>
9168  * <pre><code>
9169 boolean scriptTag = false;
9170 String cb = request.getParameter("callback");
9171 if (cb != null) {
9172     scriptTag = true;
9173     response.setContentType("text/javascript");
9174 } else {
9175     response.setContentType("application/x-json");
9176 }
9177 Writer out = response.getWriter();
9178 if (scriptTag) {
9179     out.write(cb + "(");
9180 }
9181 out.print(dataBlock.toJsonString());
9182 if (scriptTag) {
9183     out.write(");");
9184 }
9185 </pre></code>
9186  *
9187  * @constructor
9188  * @param {Object} config A configuration object.
9189  */
9190 Roo.data.ScriptTagProxy = function(config){
9191     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9192     Roo.apply(this, config);
9193     this.head = document.getElementsByTagName("head")[0];
9194 };
9195
9196 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9197
9198 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9199     /**
9200      * @cfg {String} url The URL from which to request the data object.
9201      */
9202     /**
9203      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9204      */
9205     timeout : 30000,
9206     /**
9207      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9208      * the server the name of the callback function set up by the load call to process the returned data object.
9209      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9210      * javascript output which calls this named function passing the data object as its only parameter.
9211      */
9212     callbackParam : "callback",
9213     /**
9214      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9215      * name to the request.
9216      */
9217     nocache : true,
9218
9219     /**
9220      * Load data from the configured URL, read the data object into
9221      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9222      * process that block using the passed callback.
9223      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9224      * for the request to the remote server.
9225      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9226      * object into a block of Roo.data.Records.
9227      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9228      * The function must be passed <ul>
9229      * <li>The Record block object</li>
9230      * <li>The "arg" argument from the load function</li>
9231      * <li>A boolean success indicator</li>
9232      * </ul>
9233      * @param {Object} scope The scope in which to call the callback
9234      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9235      */
9236     load : function(params, reader, callback, scope, arg){
9237         if(this.fireEvent("beforeload", this, params) !== false){
9238
9239             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9240
9241             var url = this.url;
9242             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9243             if(this.nocache){
9244                 url += "&_dc=" + (new Date().getTime());
9245             }
9246             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9247             var trans = {
9248                 id : transId,
9249                 cb : "stcCallback"+transId,
9250                 scriptId : "stcScript"+transId,
9251                 params : params,
9252                 arg : arg,
9253                 url : url,
9254                 callback : callback,
9255                 scope : scope,
9256                 reader : reader
9257             };
9258             var conn = this;
9259
9260             window[trans.cb] = function(o){
9261                 conn.handleResponse(o, trans);
9262             };
9263
9264             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9265
9266             if(this.autoAbort !== false){
9267                 this.abort();
9268             }
9269
9270             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9271
9272             var script = document.createElement("script");
9273             script.setAttribute("src", url);
9274             script.setAttribute("type", "text/javascript");
9275             script.setAttribute("id", trans.scriptId);
9276             this.head.appendChild(script);
9277
9278             this.trans = trans;
9279         }else{
9280             callback.call(scope||this, null, arg, false);
9281         }
9282     },
9283
9284     // private
9285     isLoading : function(){
9286         return this.trans ? true : false;
9287     },
9288
9289     /**
9290      * Abort the current server request.
9291      */
9292     abort : function(){
9293         if(this.isLoading()){
9294             this.destroyTrans(this.trans);
9295         }
9296     },
9297
9298     // private
9299     destroyTrans : function(trans, isLoaded){
9300         this.head.removeChild(document.getElementById(trans.scriptId));
9301         clearTimeout(trans.timeoutId);
9302         if(isLoaded){
9303             window[trans.cb] = undefined;
9304             try{
9305                 delete window[trans.cb];
9306             }catch(e){}
9307         }else{
9308             // if hasn't been loaded, wait for load to remove it to prevent script error
9309             window[trans.cb] = function(){
9310                 window[trans.cb] = undefined;
9311                 try{
9312                     delete window[trans.cb];
9313                 }catch(e){}
9314             };
9315         }
9316     },
9317
9318     // private
9319     handleResponse : function(o, trans){
9320         this.trans = false;
9321         this.destroyTrans(trans, true);
9322         var result;
9323         try {
9324             result = trans.reader.readRecords(o);
9325         }catch(e){
9326             this.fireEvent("loadexception", this, o, trans.arg, e);
9327             trans.callback.call(trans.scope||window, null, trans.arg, false);
9328             return;
9329         }
9330         this.fireEvent("load", this, o, trans.arg);
9331         trans.callback.call(trans.scope||window, result, trans.arg, true);
9332     },
9333
9334     // private
9335     handleFailure : function(trans){
9336         this.trans = false;
9337         this.destroyTrans(trans, false);
9338         this.fireEvent("loadexception", this, null, trans.arg);
9339         trans.callback.call(trans.scope||window, null, trans.arg, false);
9340     }
9341 });/*
9342  * Based on:
9343  * Ext JS Library 1.1.1
9344  * Copyright(c) 2006-2007, Ext JS, LLC.
9345  *
9346  * Originally Released Under LGPL - original licence link has changed is not relivant.
9347  *
9348  * Fork - LGPL
9349  * <script type="text/javascript">
9350  */
9351
9352 /**
9353  * @class Roo.data.JsonReader
9354  * @extends Roo.data.DataReader
9355  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9356  * based on mappings in a provided Roo.data.Record constructor.
9357  * 
9358  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9359  * in the reply previously. 
9360  * 
9361  * <p>
9362  * Example code:
9363  * <pre><code>
9364 var RecordDef = Roo.data.Record.create([
9365     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9366     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9367 ]);
9368 var myReader = new Roo.data.JsonReader({
9369     totalProperty: "results",    // The property which contains the total dataset size (optional)
9370     root: "rows",                // The property which contains an Array of row objects
9371     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9372 }, RecordDef);
9373 </code></pre>
9374  * <p>
9375  * This would consume a JSON file like this:
9376  * <pre><code>
9377 { 'results': 2, 'rows': [
9378     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9379     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9380 }
9381 </code></pre>
9382  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9383  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9384  * paged from the remote server.
9385  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9386  * @cfg {String} root name of the property which contains the Array of row objects.
9387  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9388  * @constructor
9389  * Create a new JsonReader
9390  * @param {Object} meta Metadata configuration options
9391  * @param {Object} recordType Either an Array of field definition objects,
9392  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9393  */
9394 Roo.data.JsonReader = function(meta, recordType){
9395     
9396     meta = meta || {};
9397     // set some defaults:
9398     Roo.applyIf(meta, {
9399         totalProperty: 'total',
9400         successProperty : 'success',
9401         root : 'data',
9402         id : 'id'
9403     });
9404     
9405     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9406 };
9407 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9408     
9409     /**
9410      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9411      * Used by Store query builder to append _requestMeta to params.
9412      * 
9413      */
9414     metaFromRemote : false,
9415     /**
9416      * This method is only used by a DataProxy which has retrieved data from a remote server.
9417      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9418      * @return {Object} data A data block which is used by an Roo.data.Store object as
9419      * a cache of Roo.data.Records.
9420      */
9421     read : function(response){
9422         var json = response.responseText;
9423        
9424         var o = /* eval:var:o */ eval("("+json+")");
9425         if(!o) {
9426             throw {message: "JsonReader.read: Json object not found"};
9427         }
9428         
9429         if(o.metaData){
9430             
9431             delete this.ef;
9432             this.metaFromRemote = true;
9433             this.meta = o.metaData;
9434             this.recordType = Roo.data.Record.create(o.metaData.fields);
9435             this.onMetaChange(this.meta, this.recordType, o);
9436         }
9437         return this.readRecords(o);
9438     },
9439
9440     // private function a store will implement
9441     onMetaChange : function(meta, recordType, o){
9442
9443     },
9444
9445     /**
9446          * @ignore
9447          */
9448     simpleAccess: function(obj, subsc) {
9449         return obj[subsc];
9450     },
9451
9452         /**
9453          * @ignore
9454          */
9455     getJsonAccessor: function(){
9456         var re = /[\[\.]/;
9457         return function(expr) {
9458             try {
9459                 return(re.test(expr))
9460                     ? new Function("obj", "return obj." + expr)
9461                     : function(obj){
9462                         return obj[expr];
9463                     };
9464             } catch(e){}
9465             return Roo.emptyFn;
9466         };
9467     }(),
9468
9469     /**
9470      * Create a data block containing Roo.data.Records from an XML document.
9471      * @param {Object} o An object which contains an Array of row objects in the property specified
9472      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9473      * which contains the total size of the dataset.
9474      * @return {Object} data A data block which is used by an Roo.data.Store object as
9475      * a cache of Roo.data.Records.
9476      */
9477     readRecords : function(o){
9478         /**
9479          * After any data loads, the raw JSON data is available for further custom processing.
9480          * @type Object
9481          */
9482         this.o = o;
9483         var s = this.meta, Record = this.recordType,
9484             f = Record.prototype.fields, fi = f.items, fl = f.length;
9485
9486 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
9487         if (!this.ef) {
9488             if(s.totalProperty) {
9489                     this.getTotal = this.getJsonAccessor(s.totalProperty);
9490                 }
9491                 if(s.successProperty) {
9492                     this.getSuccess = this.getJsonAccessor(s.successProperty);
9493                 }
9494                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
9495                 if (s.id) {
9496                         var g = this.getJsonAccessor(s.id);
9497                         this.getId = function(rec) {
9498                                 var r = g(rec);
9499                                 return (r === undefined || r === "") ? null : r;
9500                         };
9501                 } else {
9502                         this.getId = function(){return null;};
9503                 }
9504             this.ef = [];
9505             for(var jj = 0; jj < fl; jj++){
9506                 f = fi[jj];
9507                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
9508                 this.ef[jj] = this.getJsonAccessor(map);
9509             }
9510         }
9511
9512         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
9513         if(s.totalProperty){
9514             var vt = parseInt(this.getTotal(o), 10);
9515             if(!isNaN(vt)){
9516                 totalRecords = vt;
9517             }
9518         }
9519         if(s.successProperty){
9520             var vs = this.getSuccess(o);
9521             if(vs === false || vs === 'false'){
9522                 success = false;
9523             }
9524         }
9525         var records = [];
9526             for(var i = 0; i < c; i++){
9527                     var n = root[i];
9528                 var values = {};
9529                 var id = this.getId(n);
9530                 for(var j = 0; j < fl; j++){
9531                     f = fi[j];
9532                 var v = this.ef[j](n);
9533                 if (!f.convert) {
9534                     Roo.log('missing convert for ' + f.name);
9535                     Roo.log(f);
9536                     continue;
9537                 }
9538                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9539                 }
9540                 var record = new Record(values, id);
9541                 record.json = n;
9542                 records[i] = record;
9543             }
9544             return {
9545             raw : o,
9546                 success : success,
9547                 records : records,
9548                 totalRecords : totalRecords
9549             };
9550     }
9551 });/*
9552  * Based on:
9553  * Ext JS Library 1.1.1
9554  * Copyright(c) 2006-2007, Ext JS, LLC.
9555  *
9556  * Originally Released Under LGPL - original licence link has changed is not relivant.
9557  *
9558  * Fork - LGPL
9559  * <script type="text/javascript">
9560  */
9561
9562 /**
9563  * @class Roo.data.ArrayReader
9564  * @extends Roo.data.DataReader
9565  * Data reader class to create an Array of Roo.data.Record objects from an Array.
9566  * Each element of that Array represents a row of data fields. The
9567  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
9568  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
9569  * <p>
9570  * Example code:.
9571  * <pre><code>
9572 var RecordDef = Roo.data.Record.create([
9573     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
9574     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
9575 ]);
9576 var myReader = new Roo.data.ArrayReader({
9577     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
9578 }, RecordDef);
9579 </code></pre>
9580  * <p>
9581  * This would consume an Array like this:
9582  * <pre><code>
9583 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
9584   </code></pre>
9585  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
9586  * @constructor
9587  * Create a new JsonReader
9588  * @param {Object} meta Metadata configuration options.
9589  * @param {Object} recordType Either an Array of field definition objects
9590  * as specified to {@link Roo.data.Record#create},
9591  * or an {@link Roo.data.Record} object
9592  * created using {@link Roo.data.Record#create}.
9593  */
9594 Roo.data.ArrayReader = function(meta, recordType){
9595     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
9596 };
9597
9598 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
9599     /**
9600      * Create a data block containing Roo.data.Records from an XML document.
9601      * @param {Object} o An Array of row objects which represents the dataset.
9602      * @return {Object} data A data block which is used by an Roo.data.Store object as
9603      * a cache of Roo.data.Records.
9604      */
9605     readRecords : function(o){
9606         var sid = this.meta ? this.meta.id : null;
9607         var recordType = this.recordType, fields = recordType.prototype.fields;
9608         var records = [];
9609         var root = o;
9610             for(var i = 0; i < root.length; i++){
9611                     var n = root[i];
9612                 var values = {};
9613                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
9614                 for(var j = 0, jlen = fields.length; j < jlen; j++){
9615                 var f = fields.items[j];
9616                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
9617                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
9618                 v = f.convert(v);
9619                 values[f.name] = v;
9620             }
9621                 var record = new recordType(values, id);
9622                 record.json = n;
9623                 records[records.length] = record;
9624             }
9625             return {
9626                 records : records,
9627                 totalRecords : records.length
9628             };
9629     }
9630 });/*
9631  * - LGPL
9632  * * 
9633  */
9634
9635 /**
9636  * @class Roo.bootstrap.ComboBox
9637  * @extends Roo.bootstrap.TriggerField
9638  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
9639  * @cfg {Boolean} append (true|false) default false
9640  * @constructor
9641  * Create a new ComboBox.
9642  * @param {Object} config Configuration options
9643  */
9644 Roo.bootstrap.ComboBox = function(config){
9645     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
9646     this.addEvents({
9647         /**
9648          * @event expand
9649          * Fires when the dropdown list is expanded
9650              * @param {Roo.bootstrap.ComboBox} combo This combo box
9651              */
9652         'expand' : true,
9653         /**
9654          * @event collapse
9655          * Fires when the dropdown list is collapsed
9656              * @param {Roo.bootstrap.ComboBox} combo This combo box
9657              */
9658         'collapse' : true,
9659         /**
9660          * @event beforeselect
9661          * Fires before a list item is selected. Return false to cancel the selection.
9662              * @param {Roo.bootstrap.ComboBox} combo This combo box
9663              * @param {Roo.data.Record} record The data record returned from the underlying store
9664              * @param {Number} index The index of the selected item in the dropdown list
9665              */
9666         'beforeselect' : true,
9667         /**
9668          * @event select
9669          * Fires when a list item is selected
9670              * @param {Roo.bootstrap.ComboBox} combo This combo box
9671              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
9672              * @param {Number} index The index of the selected item in the dropdown list
9673              */
9674         'select' : true,
9675         /**
9676          * @event beforequery
9677          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
9678          * The event object passed has these properties:
9679              * @param {Roo.bootstrap.ComboBox} combo This combo box
9680              * @param {String} query The query
9681              * @param {Boolean} forceAll true to force "all" query
9682              * @param {Boolean} cancel true to cancel the query
9683              * @param {Object} e The query event object
9684              */
9685         'beforequery': true,
9686          /**
9687          * @event add
9688          * Fires when the 'add' icon is pressed (add a listener to enable add button)
9689              * @param {Roo.bootstrap.ComboBox} combo This combo box
9690              */
9691         'add' : true,
9692         /**
9693          * @event edit
9694          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9695              * @param {Roo.bootstrap.ComboBox} combo This combo box
9696              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9697              */
9698         'edit' : true,
9699         /**
9700          * @event remove
9701          * Fires when the remove value from the combobox array
9702              * @param {Roo.bootstrap.ComboBox} combo This combo box
9703              */
9704         'remove' : true
9705         
9706     });
9707     
9708     
9709     this.selectedIndex = -1;
9710     if(this.mode == 'local'){
9711         if(config.queryDelay === undefined){
9712             this.queryDelay = 10;
9713         }
9714         if(config.minChars === undefined){
9715             this.minChars = 0;
9716         }
9717     }
9718 };
9719
9720 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
9721      
9722     /**
9723      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
9724      * rendering into an Roo.Editor, defaults to false)
9725      */
9726     /**
9727      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
9728      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
9729      */
9730     /**
9731      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
9732      */
9733     /**
9734      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
9735      * the dropdown list (defaults to undefined, with no header element)
9736      */
9737
9738      /**
9739      * @cfg {String/Roo.Template} tpl The template to use to render the output
9740      */
9741      
9742      /**
9743      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
9744      */
9745     listWidth: undefined,
9746     /**
9747      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
9748      * mode = 'remote' or 'text' if mode = 'local')
9749      */
9750     displayField: undefined,
9751     /**
9752      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
9753      * mode = 'remote' or 'value' if mode = 'local'). 
9754      * Note: use of a valueField requires the user make a selection
9755      * in order for a value to be mapped.
9756      */
9757     valueField: undefined,
9758     
9759     
9760     /**
9761      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
9762      * field's data value (defaults to the underlying DOM element's name)
9763      */
9764     hiddenName: undefined,
9765     /**
9766      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
9767      */
9768     listClass: '',
9769     /**
9770      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
9771      */
9772     selectedClass: 'active',
9773     
9774     /**
9775      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9776      */
9777     shadow:'sides',
9778     /**
9779      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
9780      * anchor positions (defaults to 'tl-bl')
9781      */
9782     listAlign: 'tl-bl?',
9783     /**
9784      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
9785      */
9786     maxHeight: 300,
9787     /**
9788      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
9789      * query specified by the allQuery config option (defaults to 'query')
9790      */
9791     triggerAction: 'query',
9792     /**
9793      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
9794      * (defaults to 4, does not apply if editable = false)
9795      */
9796     minChars : 4,
9797     /**
9798      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
9799      * delay (typeAheadDelay) if it matches a known value (defaults to false)
9800      */
9801     typeAhead: false,
9802     /**
9803      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
9804      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
9805      */
9806     queryDelay: 500,
9807     /**
9808      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
9809      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
9810      */
9811     pageSize: 0,
9812     /**
9813      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
9814      * when editable = true (defaults to false)
9815      */
9816     selectOnFocus:false,
9817     /**
9818      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
9819      */
9820     queryParam: 'query',
9821     /**
9822      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
9823      * when mode = 'remote' (defaults to 'Loading...')
9824      */
9825     loadingText: 'Loading...',
9826     /**
9827      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
9828      */
9829     resizable: false,
9830     /**
9831      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
9832      */
9833     handleHeight : 8,
9834     /**
9835      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
9836      * traditional select (defaults to true)
9837      */
9838     editable: true,
9839     /**
9840      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
9841      */
9842     allQuery: '',
9843     /**
9844      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
9845      */
9846     mode: 'remote',
9847     /**
9848      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
9849      * listWidth has a higher value)
9850      */
9851     minListWidth : 70,
9852     /**
9853      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
9854      * allow the user to set arbitrary text into the field (defaults to false)
9855      */
9856     forceSelection:false,
9857     /**
9858      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
9859      * if typeAhead = true (defaults to 250)
9860      */
9861     typeAheadDelay : 250,
9862     /**
9863      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
9864      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
9865      */
9866     valueNotFoundText : undefined,
9867     /**
9868      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
9869      */
9870     blockFocus : false,
9871     
9872     /**
9873      * @cfg {Boolean} disableClear Disable showing of clear button.
9874      */
9875     disableClear : false,
9876     /**
9877      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
9878      */
9879     alwaysQuery : false,
9880     
9881     /**
9882      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
9883      */
9884     multiple : false,
9885     
9886     //private
9887     addicon : false,
9888     editicon: false,
9889     
9890     page: 0,
9891     hasQuery: false,
9892     append: false,
9893     loadNext: false,
9894     item: [],
9895     
9896     // element that contains real text value.. (when hidden is used..)
9897      
9898     // private
9899     initEvents: function(){
9900         
9901         if (!this.store) {
9902             throw "can not find store for combo";
9903         }
9904         this.store = Roo.factory(this.store, Roo.data);
9905         
9906         
9907         
9908         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
9909         
9910         
9911         if(this.hiddenName){
9912             
9913             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
9914             
9915             this.hiddenField.dom.value =
9916                 this.hiddenValue !== undefined ? this.hiddenValue :
9917                 this.value !== undefined ? this.value : '';
9918
9919             // prevent input submission
9920             this.el.dom.removeAttribute('name');
9921             this.hiddenField.dom.setAttribute('name', this.hiddenName);
9922              
9923              
9924         }
9925         //if(Roo.isGecko){
9926         //    this.el.dom.setAttribute('autocomplete', 'off');
9927         //}
9928
9929         var cls = 'x-combo-list';
9930         this.list = this.el.select('ul.dropdown-menu',true).first();
9931
9932         //this.list = new Roo.Layer({
9933         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
9934         //});
9935         
9936         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
9937         this.list.setWidth(lw);
9938         
9939         this.list.on('mouseover', this.onViewOver, this);
9940         this.list.on('mousemove', this.onViewMove, this);
9941         
9942         this.list.on('scroll', this.onViewScroll, this);
9943         
9944         /*
9945         this.list.swallowEvent('mousewheel');
9946         this.assetHeight = 0;
9947
9948         if(this.title){
9949             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
9950             this.assetHeight += this.header.getHeight();
9951         }
9952
9953         this.innerList = this.list.createChild({cls:cls+'-inner'});
9954         this.innerList.on('mouseover', this.onViewOver, this);
9955         this.innerList.on('mousemove', this.onViewMove, this);
9956         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9957         
9958         if(this.allowBlank && !this.pageSize && !this.disableClear){
9959             this.footer = this.list.createChild({cls:cls+'-ft'});
9960             this.pageTb = new Roo.Toolbar(this.footer);
9961            
9962         }
9963         if(this.pageSize){
9964             this.footer = this.list.createChild({cls:cls+'-ft'});
9965             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
9966                     {pageSize: this.pageSize});
9967             
9968         }
9969         
9970         if (this.pageTb && this.allowBlank && !this.disableClear) {
9971             var _this = this;
9972             this.pageTb.add(new Roo.Toolbar.Fill(), {
9973                 cls: 'x-btn-icon x-btn-clear',
9974                 text: '&#160;',
9975                 handler: function()
9976                 {
9977                     _this.collapse();
9978                     _this.clearValue();
9979                     _this.onSelect(false, -1);
9980                 }
9981             });
9982         }
9983         if (this.footer) {
9984             this.assetHeight += this.footer.getHeight();
9985         }
9986         */
9987             
9988         if(!this.tpl){
9989             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
9990         }
9991
9992         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
9993             singleSelect:true, store: this.store, selectedClass: this.selectedClass
9994         });
9995         //this.view.wrapEl.setDisplayed(false);
9996         this.view.on('click', this.onViewClick, this);
9997         
9998         
9999         
10000         this.store.on('beforeload', this.onBeforeLoad, this);
10001         this.store.on('load', this.onLoad, this);
10002         this.store.on('loadexception', this.onLoadException, this);
10003         /*
10004         if(this.resizable){
10005             this.resizer = new Roo.Resizable(this.list,  {
10006                pinned:true, handles:'se'
10007             });
10008             this.resizer.on('resize', function(r, w, h){
10009                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10010                 this.listWidth = w;
10011                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10012                 this.restrictHeight();
10013             }, this);
10014             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10015         }
10016         */
10017         if(!this.editable){
10018             this.editable = true;
10019             this.setEditable(false);
10020         }
10021         
10022         /*
10023         
10024         if (typeof(this.events.add.listeners) != 'undefined') {
10025             
10026             this.addicon = this.wrap.createChild(
10027                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10028        
10029             this.addicon.on('click', function(e) {
10030                 this.fireEvent('add', this);
10031             }, this);
10032         }
10033         if (typeof(this.events.edit.listeners) != 'undefined') {
10034             
10035             this.editicon = this.wrap.createChild(
10036                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10037             if (this.addicon) {
10038                 this.editicon.setStyle('margin-left', '40px');
10039             }
10040             this.editicon.on('click', function(e) {
10041                 
10042                 // we fire even  if inothing is selected..
10043                 this.fireEvent('edit', this, this.lastData );
10044                 
10045             }, this);
10046         }
10047         */
10048         
10049         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10050             "up" : function(e){
10051                 this.inKeyMode = true;
10052                 this.selectPrev();
10053             },
10054
10055             "down" : function(e){
10056                 if(!this.isExpanded()){
10057                     this.onTriggerClick();
10058                 }else{
10059                     this.inKeyMode = true;
10060                     this.selectNext();
10061                 }
10062             },
10063
10064             "enter" : function(e){
10065                 this.onViewClick();
10066                 //return true;
10067             },
10068
10069             "esc" : function(e){
10070                 this.collapse();
10071             },
10072
10073             "tab" : function(e){
10074                 this.collapse();
10075                 
10076                 if(this.fireEvent("specialkey", this, e)){
10077                     this.onViewClick(false);
10078                 }
10079                 
10080                 return true;
10081             },
10082
10083             scope : this,
10084
10085             doRelay : function(foo, bar, hname){
10086                 if(hname == 'down' || this.scope.isExpanded()){
10087                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10088                 }
10089                 return true;
10090             },
10091
10092             forceKeyDown: true
10093         });
10094         
10095         
10096         this.queryDelay = Math.max(this.queryDelay || 10,
10097                 this.mode == 'local' ? 10 : 250);
10098         
10099         
10100         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10101         
10102         if(this.typeAhead){
10103             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10104         }
10105         if(this.editable !== false){
10106             this.inputEl().on("keyup", this.onKeyUp, this);
10107         }
10108         if(this.forceSelection){
10109             this.inputEl().on('blur', this.doForce, this);
10110         }
10111         
10112         if(this.multiple){
10113             this.choices = this.el.select('ul.select2-choices', true).first();
10114             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10115         }
10116     },
10117
10118     onDestroy : function(){
10119         if(this.view){
10120             this.view.setStore(null);
10121             this.view.el.removeAllListeners();
10122             this.view.el.remove();
10123             this.view.purgeListeners();
10124         }
10125         if(this.list){
10126             this.list.dom.innerHTML  = '';
10127         }
10128         if(this.store){
10129             this.store.un('beforeload', this.onBeforeLoad, this);
10130             this.store.un('load', this.onLoad, this);
10131             this.store.un('loadexception', this.onLoadException, this);
10132         }
10133         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10134     },
10135
10136     // private
10137     fireKey : function(e){
10138         if(e.isNavKeyPress() && !this.list.isVisible()){
10139             this.fireEvent("specialkey", this, e);
10140         }
10141     },
10142
10143     // private
10144     onResize: function(w, h){
10145 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10146 //        
10147 //        if(typeof w != 'number'){
10148 //            // we do not handle it!?!?
10149 //            return;
10150 //        }
10151 //        var tw = this.trigger.getWidth();
10152 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10153 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10154 //        var x = w - tw;
10155 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10156 //            
10157 //        //this.trigger.setStyle('left', x+'px');
10158 //        
10159 //        if(this.list && this.listWidth === undefined){
10160 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10161 //            this.list.setWidth(lw);
10162 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10163 //        }
10164         
10165     
10166         
10167     },
10168
10169     /**
10170      * Allow or prevent the user from directly editing the field text.  If false is passed,
10171      * the user will only be able to select from the items defined in the dropdown list.  This method
10172      * is the runtime equivalent of setting the 'editable' config option at config time.
10173      * @param {Boolean} value True to allow the user to directly edit the field text
10174      */
10175     setEditable : function(value){
10176         if(value == this.editable){
10177             return;
10178         }
10179         this.editable = value;
10180         if(!value){
10181             this.inputEl().dom.setAttribute('readOnly', true);
10182             this.inputEl().on('mousedown', this.onTriggerClick,  this);
10183             this.inputEl().addClass('x-combo-noedit');
10184         }else{
10185             this.inputEl().dom.setAttribute('readOnly', false);
10186             this.inputEl().un('mousedown', this.onTriggerClick,  this);
10187             this.inputEl().removeClass('x-combo-noedit');
10188         }
10189     },
10190
10191     // private
10192     
10193     onBeforeLoad : function(combo,opts){
10194         if(!this.hasFocus){
10195             return;
10196         }
10197          if (!opts.add) {
10198             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
10199          }
10200         this.restrictHeight();
10201         this.selectedIndex = -1;
10202     },
10203
10204     // private
10205     onLoad : function(){
10206         
10207         this.hasQuery = false;
10208         
10209         if(!this.hasFocus){
10210             return;
10211         }
10212         
10213         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10214             this.loading.hide();
10215         }
10216         
10217         if(this.store.getCount() > 0){
10218             this.expand();
10219             this.restrictHeight();
10220             if(this.lastQuery == this.allQuery){
10221                 if(this.editable){
10222                     this.inputEl().dom.select();
10223                 }
10224                 if(!this.selectByValue(this.value, true)){
10225                     this.select(0, true);
10226                 }
10227             }else{
10228                 this.selectNext();
10229                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
10230                     this.taTask.delay(this.typeAheadDelay);
10231                 }
10232             }
10233         }else{
10234             this.onEmptyResults();
10235         }
10236         
10237         //this.el.focus();
10238     },
10239     // private
10240     onLoadException : function()
10241     {
10242         this.hasQuery = false;
10243         
10244         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10245             this.loading.hide();
10246         }
10247         
10248         this.collapse();
10249         Roo.log(this.store.reader.jsonData);
10250         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
10251             // fixme
10252             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
10253         }
10254         
10255         
10256     },
10257     // private
10258     onTypeAhead : function(){
10259         if(this.store.getCount() > 0){
10260             var r = this.store.getAt(0);
10261             var newValue = r.data[this.displayField];
10262             var len = newValue.length;
10263             var selStart = this.getRawValue().length;
10264             
10265             if(selStart != len){
10266                 this.setRawValue(newValue);
10267                 this.selectText(selStart, newValue.length);
10268             }
10269         }
10270     },
10271
10272     // private
10273     onSelect : function(record, index){
10274         
10275         if(this.fireEvent('beforeselect', this, record, index) !== false){
10276         
10277             this.setFromData(index > -1 ? record.data : false);
10278             
10279             this.collapse();
10280             this.fireEvent('select', this, record, index);
10281         }
10282     },
10283
10284     /**
10285      * Returns the currently selected field value or empty string if no value is set.
10286      * @return {String} value The selected value
10287      */
10288     getValue : function(){
10289         
10290         if(this.multiple){
10291             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
10292         }
10293         
10294         if(this.valueField){
10295             return typeof this.value != 'undefined' ? this.value : '';
10296         }else{
10297             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
10298         }
10299     },
10300
10301     /**
10302      * Clears any text/value currently set in the field
10303      */
10304     clearValue : function(){
10305         if(this.hiddenField){
10306             this.hiddenField.dom.value = '';
10307         }
10308         this.value = '';
10309         this.setRawValue('');
10310         this.lastSelectionText = '';
10311         
10312     },
10313
10314     /**
10315      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
10316      * will be displayed in the field.  If the value does not match the data value of an existing item,
10317      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
10318      * Otherwise the field will be blank (although the value will still be set).
10319      * @param {String} value The value to match
10320      */
10321     setValue : function(v){
10322         if(this.multiple){
10323             this.syncValue();
10324             return;
10325         }
10326         
10327         var text = v;
10328         if(this.valueField){
10329             var r = this.findRecord(this.valueField, v);
10330             if(r){
10331                 text = r.data[this.displayField];
10332             }else if(this.valueNotFoundText !== undefined){
10333                 text = this.valueNotFoundText;
10334             }
10335         }
10336         this.lastSelectionText = text;
10337         if(this.hiddenField){
10338             this.hiddenField.dom.value = v;
10339         }
10340         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
10341         this.value = v;
10342     },
10343     /**
10344      * @property {Object} the last set data for the element
10345      */
10346     
10347     lastData : false,
10348     /**
10349      * Sets the value of the field based on a object which is related to the record format for the store.
10350      * @param {Object} value the value to set as. or false on reset?
10351      */
10352     setFromData : function(o){
10353         
10354         if(this.multiple){
10355             this.addItem(o);
10356             return;
10357         }
10358             
10359         var dv = ''; // display value
10360         var vv = ''; // value value..
10361         this.lastData = o;
10362         if (this.displayField) {
10363             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10364         } else {
10365             // this is an error condition!!!
10366             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10367         }
10368         
10369         if(this.valueField){
10370             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
10371         }
10372         
10373         if(this.hiddenField){
10374             this.hiddenField.dom.value = vv;
10375             
10376             this.lastSelectionText = dv;
10377             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
10378             this.value = vv;
10379             return;
10380         }
10381         // no hidden field.. - we store the value in 'value', but still display
10382         // display field!!!!
10383         this.lastSelectionText = dv;
10384         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
10385         this.value = vv;
10386         
10387         
10388     },
10389     // private
10390     reset : function(){
10391         // overridden so that last data is reset..
10392         this.setValue(this.originalValue);
10393         this.clearInvalid();
10394         this.lastData = false;
10395         if (this.view) {
10396             this.view.clearSelections();
10397         }
10398     },
10399     // private
10400     findRecord : function(prop, value){
10401         var record;
10402         if(this.store.getCount() > 0){
10403             this.store.each(function(r){
10404                 if(r.data[prop] == value){
10405                     record = r;
10406                     return false;
10407                 }
10408                 return true;
10409             });
10410         }
10411         return record;
10412     },
10413     
10414     getName: function()
10415     {
10416         // returns hidden if it's set..
10417         if (!this.rendered) {return ''};
10418         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
10419         
10420     },
10421     // private
10422     onViewMove : function(e, t){
10423         this.inKeyMode = false;
10424     },
10425
10426     // private
10427     onViewOver : function(e, t){
10428         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
10429             return;
10430         }
10431         var item = this.view.findItemFromChild(t);
10432         if(item){
10433             var index = this.view.indexOf(item);
10434             this.select(index, false);
10435         }
10436     },
10437
10438     // private
10439     onViewClick : function(doFocus)
10440     {
10441         var index = this.view.getSelectedIndexes()[0];
10442         var r = this.store.getAt(index);
10443         if(r){
10444             this.onSelect(r, index);
10445         }
10446         if(doFocus !== false && !this.blockFocus){
10447             this.inputEl().focus();
10448         }
10449     },
10450
10451     // private
10452     restrictHeight : function(){
10453         //this.innerList.dom.style.height = '';
10454         //var inner = this.innerList.dom;
10455         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
10456         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
10457         //this.list.beginUpdate();
10458         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
10459         this.list.alignTo(this.inputEl(), this.listAlign);
10460         //this.list.endUpdate();
10461     },
10462
10463     // private
10464     onEmptyResults : function(){
10465         this.collapse();
10466     },
10467
10468     /**
10469      * Returns true if the dropdown list is expanded, else false.
10470      */
10471     isExpanded : function(){
10472         return this.list.isVisible();
10473     },
10474
10475     /**
10476      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
10477      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
10478      * @param {String} value The data value of the item to select
10479      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
10480      * selected item if it is not currently in view (defaults to true)
10481      * @return {Boolean} True if the value matched an item in the list, else false
10482      */
10483     selectByValue : function(v, scrollIntoView){
10484         if(v !== undefined && v !== null){
10485             var r = this.findRecord(this.valueField || this.displayField, v);
10486             if(r){
10487                 this.select(this.store.indexOf(r), scrollIntoView);
10488                 return true;
10489             }
10490         }
10491         return false;
10492     },
10493
10494     /**
10495      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
10496      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
10497      * @param {Number} index The zero-based index of the list item to select
10498      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
10499      * selected item if it is not currently in view (defaults to true)
10500      */
10501     select : function(index, scrollIntoView){
10502         this.selectedIndex = index;
10503         this.view.select(index);
10504         if(scrollIntoView !== false){
10505             var el = this.view.getNode(index);
10506             if(el){
10507                 //this.innerList.scrollChildIntoView(el, false);
10508                 
10509             }
10510         }
10511     },
10512
10513     // private
10514     selectNext : function(){
10515         var ct = this.store.getCount();
10516         if(ct > 0){
10517             if(this.selectedIndex == -1){
10518                 this.select(0);
10519             }else if(this.selectedIndex < ct-1){
10520                 this.select(this.selectedIndex+1);
10521             }
10522         }
10523     },
10524
10525     // private
10526     selectPrev : function(){
10527         var ct = this.store.getCount();
10528         if(ct > 0){
10529             if(this.selectedIndex == -1){
10530                 this.select(0);
10531             }else if(this.selectedIndex != 0){
10532                 this.select(this.selectedIndex-1);
10533             }
10534         }
10535     },
10536
10537     // private
10538     onKeyUp : function(e){
10539         if(this.editable !== false && !e.isSpecialKey()){
10540             this.lastKey = e.getKey();
10541             this.dqTask.delay(this.queryDelay);
10542         }
10543     },
10544
10545     // private
10546     validateBlur : function(){
10547         return !this.list || !this.list.isVisible();   
10548     },
10549
10550     // private
10551     initQuery : function(){
10552         this.doQuery(this.getRawValue());
10553     },
10554
10555     // private
10556     doForce : function(){
10557         if(this.inputEl().dom.value.length > 0){
10558             this.inputEl().dom.value =
10559                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
10560              
10561         }
10562     },
10563
10564     /**
10565      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
10566      * query allowing the query action to be canceled if needed.
10567      * @param {String} query The SQL query to execute
10568      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
10569      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
10570      * saved in the current store (defaults to false)
10571      */
10572     doQuery : function(q, forceAll){
10573         
10574         if(q === undefined || q === null){
10575             q = '';
10576         }
10577         var qe = {
10578             query: q,
10579             forceAll: forceAll,
10580             combo: this,
10581             cancel:false
10582         };
10583         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
10584             return false;
10585         }
10586         q = qe.query;
10587         
10588         forceAll = qe.forceAll;
10589         if(forceAll === true || (q.length >= this.minChars)){
10590             
10591             this.hasQuery = true;
10592             
10593             if(this.lastQuery != q || this.alwaysQuery){
10594                 this.lastQuery = q;
10595                 if(this.mode == 'local'){
10596                     this.selectedIndex = -1;
10597                     if(forceAll){
10598                         this.store.clearFilter();
10599                     }else{
10600                         this.store.filter(this.displayField, q);
10601                     }
10602                     this.onLoad();
10603                 }else{
10604                     this.store.baseParams[this.queryParam] = q;
10605                     
10606                     var options = {params : this.getParams(q)};
10607                     
10608                     if(this.loadNext){
10609                         options.add = true;
10610                         options.params.start = this.page * this.pageSize;
10611                     }
10612                     
10613                     this.store.load(options);
10614                     this.expand();
10615                 }
10616             }else{
10617                 this.selectedIndex = -1;
10618                 this.onLoad();   
10619             }
10620         }
10621         
10622         this.loadNext = false;
10623     },
10624
10625     // private
10626     getParams : function(q){
10627         var p = {};
10628         //p[this.queryParam] = q;
10629         
10630         if(this.pageSize){
10631             p.start = 0;
10632             p.limit = this.pageSize;
10633         }
10634         return p;
10635     },
10636
10637     /**
10638      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
10639      */
10640     collapse : function(){
10641         if(!this.isExpanded()){
10642             return;
10643         }
10644         
10645         this.list.hide();
10646         Roo.get(document).un('mousedown', this.collapseIf, this);
10647         Roo.get(document).un('mousewheel', this.collapseIf, this);
10648         if (!this.editable) {
10649             Roo.get(document).un('keydown', this.listKeyPress, this);
10650         }
10651         this.fireEvent('collapse', this);
10652     },
10653
10654     // private
10655     collapseIf : function(e){
10656         var in_combo  = e.within(this.el);
10657         var in_list =  e.within(this.list);
10658         
10659         if (in_combo || in_list) {
10660             //e.stopPropagation();
10661             return;
10662         }
10663
10664         this.collapse();
10665         
10666     },
10667
10668     /**
10669      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
10670      */
10671     expand : function(){
10672        
10673         if(this.isExpanded() || !this.hasFocus){
10674             return;
10675         }
10676          Roo.log('expand');
10677         this.list.alignTo(this.inputEl(), this.listAlign);
10678         this.list.show();
10679         Roo.get(document).on('mousedown', this.collapseIf, this);
10680         Roo.get(document).on('mousewheel', this.collapseIf, this);
10681         if (!this.editable) {
10682             Roo.get(document).on('keydown', this.listKeyPress, this);
10683         }
10684         
10685         this.fireEvent('expand', this);
10686     },
10687
10688     // private
10689     // Implements the default empty TriggerField.onTriggerClick function
10690     onTriggerClick : function()
10691     {
10692         Roo.log('trigger click');
10693         
10694         if(this.disabled){
10695             return;
10696         }
10697         
10698         this.page = 0;
10699         this.loadNext = false;
10700         
10701         if(this.isExpanded()){
10702             this.collapse();
10703             if (!this.blockFocus) {
10704                 this.inputEl().focus();
10705             }
10706             
10707         }else {
10708             this.hasFocus = true;
10709             if(this.triggerAction == 'all') {
10710                 this.doQuery(this.allQuery, true);
10711             } else {
10712                 this.doQuery(this.getRawValue());
10713             }
10714             if (!this.blockFocus) {
10715                 this.inputEl().focus();
10716             }
10717         }
10718     },
10719     listKeyPress : function(e)
10720     {
10721         //Roo.log('listkeypress');
10722         // scroll to first matching element based on key pres..
10723         if (e.isSpecialKey()) {
10724             return false;
10725         }
10726         var k = String.fromCharCode(e.getKey()).toUpperCase();
10727         //Roo.log(k);
10728         var match  = false;
10729         var csel = this.view.getSelectedNodes();
10730         var cselitem = false;
10731         if (csel.length) {
10732             var ix = this.view.indexOf(csel[0]);
10733             cselitem  = this.store.getAt(ix);
10734             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
10735                 cselitem = false;
10736             }
10737             
10738         }
10739         
10740         this.store.each(function(v) { 
10741             if (cselitem) {
10742                 // start at existing selection.
10743                 if (cselitem.id == v.id) {
10744                     cselitem = false;
10745                 }
10746                 return true;
10747             }
10748                 
10749             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
10750                 match = this.store.indexOf(v);
10751                 return false;
10752             }
10753             return true;
10754         }, this);
10755         
10756         if (match === false) {
10757             return true; // no more action?
10758         }
10759         // scroll to?
10760         this.view.select(match);
10761         var sn = Roo.get(this.view.getSelectedNodes()[0])
10762         //sn.scrollIntoView(sn.dom.parentNode, false);
10763     },
10764     
10765     onViewScroll : function(e, t){
10766         
10767         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
10768             return;
10769         }
10770         
10771         this.hasQuery = true;
10772         
10773         this.loading = this.list.select('.loading', true).first();
10774         
10775         if(this.loading === null){
10776             this.list.createChild({
10777                 tag: 'div',
10778                 cls: 'loading select2-more-results select2-active',
10779                 html: 'Loading more results...'
10780             })
10781             
10782             this.loading = this.list.select('.loading', true).first();
10783             
10784             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
10785             
10786             this.loading.hide();
10787         }
10788         
10789         this.loading.show();
10790         
10791         var _combo = this;
10792         
10793         this.page++;
10794         this.loadNext = true;
10795         
10796         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
10797         
10798         return;
10799     },
10800     
10801     addItem : function(o)
10802     {   
10803         var dv = ''; // display value
10804         
10805         if (this.displayField) {
10806             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10807         } else {
10808             // this is an error condition!!!
10809             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10810         }
10811         
10812         if(!dv.length){
10813             return;
10814         }
10815         
10816         var choice = this.choices.createChild({
10817             tag: 'li',
10818             cls: 'select2-search-choice',
10819             cn: [
10820                 {
10821                     tag: 'div',
10822                     html: dv
10823                 },
10824                 {
10825                     tag: 'a',
10826                     href: '#',
10827                     cls: 'select2-search-choice-close',
10828                     tabindex: '-1'
10829                 }
10830             ]
10831             
10832         }, this.searchField);
10833         
10834         var close = choice.select('a.select2-search-choice-close', true).first()
10835         
10836         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
10837         
10838         this.item.push(o);
10839         this.lastData = o;
10840         
10841         this.syncValue();
10842         
10843         this.inputEl().dom.value = '';
10844         
10845     },
10846     
10847     onRemoveItem : function(e, _self, o)
10848     {
10849         e.preventDefault();
10850         var index = this.item.indexOf(o.data) * 1;
10851         
10852         if( index < 0){
10853             Roo.log('not this item?!');
10854             return;
10855         }
10856         
10857         this.item.splice(index, 1);
10858         o.item.remove();
10859         
10860         this.syncValue();
10861         
10862         this.fireEvent('remove', this, e);
10863         
10864     },
10865     
10866     syncValue : function()
10867     {
10868         if(!this.item.length){
10869             this.clearValue();
10870             return;
10871         }
10872             
10873         var value = [];
10874         var _this = this;
10875         Roo.each(this.item, function(i){
10876             if(_this.valueField){
10877                 value.push(i[_this.valueField]);
10878                 return;
10879             }
10880
10881             value.push(i);
10882         });
10883
10884         this.value = value.join(',');
10885
10886         if(this.hiddenField){
10887             this.hiddenField.dom.value = this.value;
10888         }
10889     },
10890     
10891     clearItem : function()
10892     {
10893         if(!this.multiple){
10894             return;
10895         }
10896         
10897         this.item = [];
10898         
10899         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
10900            c.remove();
10901         });
10902         
10903         this.syncValue();
10904     }
10905     
10906     
10907
10908     /** 
10909     * @cfg {Boolean} grow 
10910     * @hide 
10911     */
10912     /** 
10913     * @cfg {Number} growMin 
10914     * @hide 
10915     */
10916     /** 
10917     * @cfg {Number} growMax 
10918     * @hide 
10919     */
10920     /**
10921      * @hide
10922      * @method autoSize
10923      */
10924 });
10925 /*
10926  * Based on:
10927  * Ext JS Library 1.1.1
10928  * Copyright(c) 2006-2007, Ext JS, LLC.
10929  *
10930  * Originally Released Under LGPL - original licence link has changed is not relivant.
10931  *
10932  * Fork - LGPL
10933  * <script type="text/javascript">
10934  */
10935
10936 /**
10937  * @class Roo.View
10938  * @extends Roo.util.Observable
10939  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
10940  * This class also supports single and multi selection modes. <br>
10941  * Create a data model bound view:
10942  <pre><code>
10943  var store = new Roo.data.Store(...);
10944
10945  var view = new Roo.View({
10946     el : "my-element",
10947     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
10948  
10949     singleSelect: true,
10950     selectedClass: "ydataview-selected",
10951     store: store
10952  });
10953
10954  // listen for node click?
10955  view.on("click", function(vw, index, node, e){
10956  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
10957  });
10958
10959  // load XML data
10960  dataModel.load("foobar.xml");
10961  </code></pre>
10962  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
10963  * <br><br>
10964  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
10965  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
10966  * 
10967  * Note: old style constructor is still suported (container, template, config)
10968  * 
10969  * @constructor
10970  * Create a new View
10971  * @param {Object} config The config object
10972  * 
10973  */
10974 Roo.View = function(config, depreciated_tpl, depreciated_config){
10975     
10976     if (typeof(depreciated_tpl) == 'undefined') {
10977         // new way.. - universal constructor.
10978         Roo.apply(this, config);
10979         this.el  = Roo.get(this.el);
10980     } else {
10981         // old format..
10982         this.el  = Roo.get(config);
10983         this.tpl = depreciated_tpl;
10984         Roo.apply(this, depreciated_config);
10985     }
10986     this.wrapEl  = this.el.wrap().wrap();
10987     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
10988     
10989     
10990     if(typeof(this.tpl) == "string"){
10991         this.tpl = new Roo.Template(this.tpl);
10992     } else {
10993         // support xtype ctors..
10994         this.tpl = new Roo.factory(this.tpl, Roo);
10995     }
10996     
10997     
10998     this.tpl.compile();
10999    
11000   
11001     
11002      
11003     /** @private */
11004     this.addEvents({
11005         /**
11006          * @event beforeclick
11007          * Fires before a click is processed. Returns false to cancel the default action.
11008          * @param {Roo.View} this
11009          * @param {Number} index The index of the target node
11010          * @param {HTMLElement} node The target node
11011          * @param {Roo.EventObject} e The raw event object
11012          */
11013             "beforeclick" : true,
11014         /**
11015          * @event click
11016          * Fires when a template node is clicked.
11017          * @param {Roo.View} this
11018          * @param {Number} index The index of the target node
11019          * @param {HTMLElement} node The target node
11020          * @param {Roo.EventObject} e The raw event object
11021          */
11022             "click" : true,
11023         /**
11024          * @event dblclick
11025          * Fires when a template node is double clicked.
11026          * @param {Roo.View} this
11027          * @param {Number} index The index of the target node
11028          * @param {HTMLElement} node The target node
11029          * @param {Roo.EventObject} e The raw event object
11030          */
11031             "dblclick" : true,
11032         /**
11033          * @event contextmenu
11034          * Fires when a template node is right clicked.
11035          * @param {Roo.View} this
11036          * @param {Number} index The index of the target node
11037          * @param {HTMLElement} node The target node
11038          * @param {Roo.EventObject} e The raw event object
11039          */
11040             "contextmenu" : true,
11041         /**
11042          * @event selectionchange
11043          * Fires when the selected nodes change.
11044          * @param {Roo.View} this
11045          * @param {Array} selections Array of the selected nodes
11046          */
11047             "selectionchange" : true,
11048     
11049         /**
11050          * @event beforeselect
11051          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
11052          * @param {Roo.View} this
11053          * @param {HTMLElement} node The node to be selected
11054          * @param {Array} selections Array of currently selected nodes
11055          */
11056             "beforeselect" : true,
11057         /**
11058          * @event preparedata
11059          * Fires on every row to render, to allow you to change the data.
11060          * @param {Roo.View} this
11061          * @param {Object} data to be rendered (change this)
11062          */
11063           "preparedata" : true
11064           
11065           
11066         });
11067
11068
11069
11070     this.el.on({
11071         "click": this.onClick,
11072         "dblclick": this.onDblClick,
11073         "contextmenu": this.onContextMenu,
11074         scope:this
11075     });
11076
11077     this.selections = [];
11078     this.nodes = [];
11079     this.cmp = new Roo.CompositeElementLite([]);
11080     if(this.store){
11081         this.store = Roo.factory(this.store, Roo.data);
11082         this.setStore(this.store, true);
11083     }
11084     
11085     if ( this.footer && this.footer.xtype) {
11086            
11087          var fctr = this.wrapEl.appendChild(document.createElement("div"));
11088         
11089         this.footer.dataSource = this.store
11090         this.footer.container = fctr;
11091         this.footer = Roo.factory(this.footer, Roo);
11092         fctr.insertFirst(this.el);
11093         
11094         // this is a bit insane - as the paging toolbar seems to detach the el..
11095 //        dom.parentNode.parentNode.parentNode
11096          // they get detached?
11097     }
11098     
11099     
11100     Roo.View.superclass.constructor.call(this);
11101     
11102     
11103 };
11104
11105 Roo.extend(Roo.View, Roo.util.Observable, {
11106     
11107      /**
11108      * @cfg {Roo.data.Store} store Data store to load data from.
11109      */
11110     store : false,
11111     
11112     /**
11113      * @cfg {String|Roo.Element} el The container element.
11114      */
11115     el : '',
11116     
11117     /**
11118      * @cfg {String|Roo.Template} tpl The template used by this View 
11119      */
11120     tpl : false,
11121     /**
11122      * @cfg {String} dataName the named area of the template to use as the data area
11123      *                          Works with domtemplates roo-name="name"
11124      */
11125     dataName: false,
11126     /**
11127      * @cfg {String} selectedClass The css class to add to selected nodes
11128      */
11129     selectedClass : "x-view-selected",
11130      /**
11131      * @cfg {String} emptyText The empty text to show when nothing is loaded.
11132      */
11133     emptyText : "",
11134     
11135     /**
11136      * @cfg {String} text to display on mask (default Loading)
11137      */
11138     mask : false,
11139     /**
11140      * @cfg {Boolean} multiSelect Allow multiple selection
11141      */
11142     multiSelect : false,
11143     /**
11144      * @cfg {Boolean} singleSelect Allow single selection
11145      */
11146     singleSelect:  false,
11147     
11148     /**
11149      * @cfg {Boolean} toggleSelect - selecting 
11150      */
11151     toggleSelect : false,
11152     
11153     /**
11154      * Returns the element this view is bound to.
11155      * @return {Roo.Element}
11156      */
11157     getEl : function(){
11158         return this.wrapEl;
11159     },
11160     
11161     
11162
11163     /**
11164      * Refreshes the view. - called by datachanged on the store. - do not call directly.
11165      */
11166     refresh : function(){
11167         Roo.log('refresh');
11168         var t = this.tpl;
11169         
11170         // if we are using something like 'domtemplate', then
11171         // the what gets used is:
11172         // t.applySubtemplate(NAME, data, wrapping data..)
11173         // the outer template then get' applied with
11174         //     the store 'extra data'
11175         // and the body get's added to the
11176         //      roo-name="data" node?
11177         //      <span class='roo-tpl-{name}'></span> ?????
11178         
11179         
11180         
11181         this.clearSelections();
11182         this.el.update("");
11183         var html = [];
11184         var records = this.store.getRange();
11185         if(records.length < 1) {
11186             
11187             // is this valid??  = should it render a template??
11188             
11189             this.el.update(this.emptyText);
11190             return;
11191         }
11192         var el = this.el;
11193         if (this.dataName) {
11194             this.el.update(t.apply(this.store.meta)); //????
11195             el = this.el.child('.roo-tpl-' + this.dataName);
11196         }
11197         
11198         for(var i = 0, len = records.length; i < len; i++){
11199             var data = this.prepareData(records[i].data, i, records[i]);
11200             this.fireEvent("preparedata", this, data, i, records[i]);
11201             html[html.length] = Roo.util.Format.trim(
11202                 this.dataName ?
11203                     t.applySubtemplate(this.dataName, data, this.store.meta) :
11204                     t.apply(data)
11205             );
11206         }
11207         
11208         
11209         
11210         el.update(html.join(""));
11211         this.nodes = el.dom.childNodes;
11212         this.updateIndexes(0);
11213     },
11214     
11215
11216     /**
11217      * Function to override to reformat the data that is sent to
11218      * the template for each node.
11219      * DEPRICATED - use the preparedata event handler.
11220      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
11221      * a JSON object for an UpdateManager bound view).
11222      */
11223     prepareData : function(data, index, record)
11224     {
11225         this.fireEvent("preparedata", this, data, index, record);
11226         return data;
11227     },
11228
11229     onUpdate : function(ds, record){
11230          Roo.log('on update');   
11231         this.clearSelections();
11232         var index = this.store.indexOf(record);
11233         var n = this.nodes[index];
11234         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
11235         n.parentNode.removeChild(n);
11236         this.updateIndexes(index, index);
11237     },
11238
11239     
11240     
11241 // --------- FIXME     
11242     onAdd : function(ds, records, index)
11243     {
11244         Roo.log(['on Add', ds, records, index] );        
11245         this.clearSelections();
11246         if(this.nodes.length == 0){
11247             this.refresh();
11248             return;
11249         }
11250         var n = this.nodes[index];
11251         for(var i = 0, len = records.length; i < len; i++){
11252             var d = this.prepareData(records[i].data, i, records[i]);
11253             if(n){
11254                 this.tpl.insertBefore(n, d);
11255             }else{
11256                 
11257                 this.tpl.append(this.el, d);
11258             }
11259         }
11260         this.updateIndexes(index);
11261     },
11262
11263     onRemove : function(ds, record, index){
11264         Roo.log('onRemove');
11265         this.clearSelections();
11266         var el = this.dataName  ?
11267             this.el.child('.roo-tpl-' + this.dataName) :
11268             this.el; 
11269         
11270         el.dom.removeChild(this.nodes[index]);
11271         this.updateIndexes(index);
11272     },
11273
11274     /**
11275      * Refresh an individual node.
11276      * @param {Number} index
11277      */
11278     refreshNode : function(index){
11279         this.onUpdate(this.store, this.store.getAt(index));
11280     },
11281
11282     updateIndexes : function(startIndex, endIndex){
11283         var ns = this.nodes;
11284         startIndex = startIndex || 0;
11285         endIndex = endIndex || ns.length - 1;
11286         for(var i = startIndex; i <= endIndex; i++){
11287             ns[i].nodeIndex = i;
11288         }
11289     },
11290
11291     /**
11292      * Changes the data store this view uses and refresh the view.
11293      * @param {Store} store
11294      */
11295     setStore : function(store, initial){
11296         if(!initial && this.store){
11297             this.store.un("datachanged", this.refresh);
11298             this.store.un("add", this.onAdd);
11299             this.store.un("remove", this.onRemove);
11300             this.store.un("update", this.onUpdate);
11301             this.store.un("clear", this.refresh);
11302             this.store.un("beforeload", this.onBeforeLoad);
11303             this.store.un("load", this.onLoad);
11304             this.store.un("loadexception", this.onLoad);
11305         }
11306         if(store){
11307           
11308             store.on("datachanged", this.refresh, this);
11309             store.on("add", this.onAdd, this);
11310             store.on("remove", this.onRemove, this);
11311             store.on("update", this.onUpdate, this);
11312             store.on("clear", this.refresh, this);
11313             store.on("beforeload", this.onBeforeLoad, this);
11314             store.on("load", this.onLoad, this);
11315             store.on("loadexception", this.onLoad, this);
11316         }
11317         
11318         if(store){
11319             this.refresh();
11320         }
11321     },
11322     /**
11323      * onbeforeLoad - masks the loading area.
11324      *
11325      */
11326     onBeforeLoad : function(store,opts)
11327     {
11328          Roo.log('onBeforeLoad');   
11329         if (!opts.add) {
11330             this.el.update("");
11331         }
11332         this.el.mask(this.mask ? this.mask : "Loading" ); 
11333     },
11334     onLoad : function ()
11335     {
11336         this.el.unmask();
11337     },
11338     
11339
11340     /**
11341      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
11342      * @param {HTMLElement} node
11343      * @return {HTMLElement} The template node
11344      */
11345     findItemFromChild : function(node){
11346         var el = this.dataName  ?
11347             this.el.child('.roo-tpl-' + this.dataName,true) :
11348             this.el.dom; 
11349         
11350         if(!node || node.parentNode == el){
11351                     return node;
11352             }
11353             var p = node.parentNode;
11354             while(p && p != el){
11355             if(p.parentNode == el){
11356                 return p;
11357             }
11358             p = p.parentNode;
11359         }
11360             return null;
11361     },
11362
11363     /** @ignore */
11364     onClick : function(e){
11365         var item = this.findItemFromChild(e.getTarget());
11366         if(item){
11367             var index = this.indexOf(item);
11368             if(this.onItemClick(item, index, e) !== false){
11369                 this.fireEvent("click", this, index, item, e);
11370             }
11371         }else{
11372             this.clearSelections();
11373         }
11374     },
11375
11376     /** @ignore */
11377     onContextMenu : function(e){
11378         var item = this.findItemFromChild(e.getTarget());
11379         if(item){
11380             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
11381         }
11382     },
11383
11384     /** @ignore */
11385     onDblClick : function(e){
11386         var item = this.findItemFromChild(e.getTarget());
11387         if(item){
11388             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
11389         }
11390     },
11391
11392     onItemClick : function(item, index, e)
11393     {
11394         if(this.fireEvent("beforeclick", this, index, item, e) === false){
11395             return false;
11396         }
11397         if (this.toggleSelect) {
11398             var m = this.isSelected(item) ? 'unselect' : 'select';
11399             Roo.log(m);
11400             var _t = this;
11401             _t[m](item, true, false);
11402             return true;
11403         }
11404         if(this.multiSelect || this.singleSelect){
11405             if(this.multiSelect && e.shiftKey && this.lastSelection){
11406                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
11407             }else{
11408                 this.select(item, this.multiSelect && e.ctrlKey);
11409                 this.lastSelection = item;
11410             }
11411             e.preventDefault();
11412         }
11413         return true;
11414     },
11415
11416     /**
11417      * Get the number of selected nodes.
11418      * @return {Number}
11419      */
11420     getSelectionCount : function(){
11421         return this.selections.length;
11422     },
11423
11424     /**
11425      * Get the currently selected nodes.
11426      * @return {Array} An array of HTMLElements
11427      */
11428     getSelectedNodes : function(){
11429         return this.selections;
11430     },
11431
11432     /**
11433      * Get the indexes of the selected nodes.
11434      * @return {Array}
11435      */
11436     getSelectedIndexes : function(){
11437         var indexes = [], s = this.selections;
11438         for(var i = 0, len = s.length; i < len; i++){
11439             indexes.push(s[i].nodeIndex);
11440         }
11441         return indexes;
11442     },
11443
11444     /**
11445      * Clear all selections
11446      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
11447      */
11448     clearSelections : function(suppressEvent){
11449         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
11450             this.cmp.elements = this.selections;
11451             this.cmp.removeClass(this.selectedClass);
11452             this.selections = [];
11453             if(!suppressEvent){
11454                 this.fireEvent("selectionchange", this, this.selections);
11455             }
11456         }
11457     },
11458
11459     /**
11460      * Returns true if the passed node is selected
11461      * @param {HTMLElement/Number} node The node or node index
11462      * @return {Boolean}
11463      */
11464     isSelected : function(node){
11465         var s = this.selections;
11466         if(s.length < 1){
11467             return false;
11468         }
11469         node = this.getNode(node);
11470         return s.indexOf(node) !== -1;
11471     },
11472
11473     /**
11474      * Selects nodes.
11475      * @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
11476      * @param {Boolean} keepExisting (optional) true to keep existing selections
11477      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
11478      */
11479     select : function(nodeInfo, keepExisting, suppressEvent){
11480         if(nodeInfo instanceof Array){
11481             if(!keepExisting){
11482                 this.clearSelections(true);
11483             }
11484             for(var i = 0, len = nodeInfo.length; i < len; i++){
11485                 this.select(nodeInfo[i], true, true);
11486             }
11487             return;
11488         } 
11489         var node = this.getNode(nodeInfo);
11490         if(!node || this.isSelected(node)){
11491             return; // already selected.
11492         }
11493         if(!keepExisting){
11494             this.clearSelections(true);
11495         }
11496         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
11497             Roo.fly(node).addClass(this.selectedClass);
11498             this.selections.push(node);
11499             if(!suppressEvent){
11500                 this.fireEvent("selectionchange", this, this.selections);
11501             }
11502         }
11503         
11504         
11505     },
11506       /**
11507      * Unselects nodes.
11508      * @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
11509      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
11510      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
11511      */
11512     unselect : function(nodeInfo, keepExisting, suppressEvent)
11513     {
11514         if(nodeInfo instanceof Array){
11515             Roo.each(this.selections, function(s) {
11516                 this.unselect(s, nodeInfo);
11517             }, this);
11518             return;
11519         }
11520         var node = this.getNode(nodeInfo);
11521         if(!node || !this.isSelected(node)){
11522             Roo.log("not selected");
11523             return; // not selected.
11524         }
11525         // fireevent???
11526         var ns = [];
11527         Roo.each(this.selections, function(s) {
11528             if (s == node ) {
11529                 Roo.fly(node).removeClass(this.selectedClass);
11530
11531                 return;
11532             }
11533             ns.push(s);
11534         },this);
11535         
11536         this.selections= ns;
11537         this.fireEvent("selectionchange", this, this.selections);
11538     },
11539
11540     /**
11541      * Gets a template node.
11542      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
11543      * @return {HTMLElement} The node or null if it wasn't found
11544      */
11545     getNode : function(nodeInfo){
11546         if(typeof nodeInfo == "string"){
11547             return document.getElementById(nodeInfo);
11548         }else if(typeof nodeInfo == "number"){
11549             return this.nodes[nodeInfo];
11550         }
11551         return nodeInfo;
11552     },
11553
11554     /**
11555      * Gets a range template nodes.
11556      * @param {Number} startIndex
11557      * @param {Number} endIndex
11558      * @return {Array} An array of nodes
11559      */
11560     getNodes : function(start, end){
11561         var ns = this.nodes;
11562         start = start || 0;
11563         end = typeof end == "undefined" ? ns.length - 1 : end;
11564         var nodes = [];
11565         if(start <= end){
11566             for(var i = start; i <= end; i++){
11567                 nodes.push(ns[i]);
11568             }
11569         } else{
11570             for(var i = start; i >= end; i--){
11571                 nodes.push(ns[i]);
11572             }
11573         }
11574         return nodes;
11575     },
11576
11577     /**
11578      * Finds the index of the passed node
11579      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
11580      * @return {Number} The index of the node or -1
11581      */
11582     indexOf : function(node){
11583         node = this.getNode(node);
11584         if(typeof node.nodeIndex == "number"){
11585             return node.nodeIndex;
11586         }
11587         var ns = this.nodes;
11588         for(var i = 0, len = ns.length; i < len; i++){
11589             if(ns[i] == node){
11590                 return i;
11591             }
11592         }
11593         return -1;
11594     }
11595 });
11596 /*
11597  * - LGPL
11598  *
11599  * based on jquery fullcalendar
11600  * 
11601  */
11602
11603 Roo.bootstrap = Roo.bootstrap || {};
11604 /**
11605  * @class Roo.bootstrap.Calendar
11606  * @extends Roo.bootstrap.Component
11607  * Bootstrap Calendar class
11608  * @cfg {Boolean} loadMask (true|false) default false
11609  * @cfg {Object} header generate the user specific header of the calendar, default false
11610
11611  * @constructor
11612  * Create a new Container
11613  * @param {Object} config The config object
11614  */
11615
11616
11617
11618 Roo.bootstrap.Calendar = function(config){
11619     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
11620      this.addEvents({
11621         /**
11622              * @event select
11623              * Fires when a date is selected
11624              * @param {DatePicker} this
11625              * @param {Date} date The selected date
11626              */
11627         'select': true,
11628         /**
11629              * @event monthchange
11630              * Fires when the displayed month changes 
11631              * @param {DatePicker} this
11632              * @param {Date} date The selected month
11633              */
11634         'monthchange': true,
11635         /**
11636              * @event evententer
11637              * Fires when mouse over an event
11638              * @param {Calendar} this
11639              * @param {event} Event
11640              */
11641         'evententer': true,
11642         /**
11643              * @event eventleave
11644              * Fires when the mouse leaves an
11645              * @param {Calendar} this
11646              * @param {event}
11647              */
11648         'eventleave': true,
11649         /**
11650              * @event eventclick
11651              * Fires when the mouse click an
11652              * @param {Calendar} this
11653              * @param {event}
11654              */
11655         'eventclick': true
11656         
11657     });
11658
11659 };
11660
11661 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
11662     
11663      /**
11664      * @cfg {Number} startDay
11665      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
11666      */
11667     startDay : 0,
11668     
11669     loadMask : false,
11670     
11671     header : false,
11672       
11673     getAutoCreate : function(){
11674         
11675         
11676         var fc_button = function(name, corner, style, content ) {
11677             return Roo.apply({},{
11678                 tag : 'span',
11679                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
11680                          (corner.length ?
11681                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
11682                             ''
11683                         ),
11684                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
11685                 unselectable: 'on'
11686             });
11687         };
11688         
11689         var header = {};
11690         
11691         if(!this.header){
11692             header = {
11693                 tag : 'table',
11694                 cls : 'fc-header',
11695                 style : 'width:100%',
11696                 cn : [
11697                     {
11698                         tag: 'tr',
11699                         cn : [
11700                             {
11701                                 tag : 'td',
11702                                 cls : 'fc-header-left',
11703                                 cn : [
11704                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
11705                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
11706                                     { tag: 'span', cls: 'fc-header-space' },
11707                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
11708
11709
11710                                 ]
11711                             },
11712
11713                             {
11714                                 tag : 'td',
11715                                 cls : 'fc-header-center',
11716                                 cn : [
11717                                     {
11718                                         tag: 'span',
11719                                         cls: 'fc-header-title',
11720                                         cn : {
11721                                             tag: 'H2',
11722                                             html : 'month / year'
11723                                         }
11724                                     }
11725
11726                                 ]
11727                             },
11728                             {
11729                                 tag : 'td',
11730                                 cls : 'fc-header-right',
11731                                 cn : [
11732                               /*      fc_button('month', 'left', '', 'month' ),
11733                                     fc_button('week', '', '', 'week' ),
11734                                     fc_button('day', 'right', '', 'day' )
11735                                 */    
11736
11737                                 ]
11738                             }
11739
11740                         ]
11741                     }
11742                 ]
11743             };
11744         }
11745         
11746         header = this.header;
11747         
11748        
11749         var cal_heads = function() {
11750             var ret = [];
11751             // fixme - handle this.
11752             
11753             for (var i =0; i < Date.dayNames.length; i++) {
11754                 var d = Date.dayNames[i];
11755                 ret.push({
11756                     tag: 'th',
11757                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
11758                     html : d.substring(0,3)
11759                 });
11760                 
11761             }
11762             ret[0].cls += ' fc-first';
11763             ret[6].cls += ' fc-last';
11764             return ret;
11765         };
11766         var cal_cell = function(n) {
11767             return  {
11768                 tag: 'td',
11769                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
11770                 cn : [
11771                     {
11772                         cn : [
11773                             {
11774                                 cls: 'fc-day-number',
11775                                 html: 'D'
11776                             },
11777                             {
11778                                 cls: 'fc-day-content',
11779                              
11780                                 cn : [
11781                                      {
11782                                         style: 'position: relative;' // height: 17px;
11783                                     }
11784                                 ]
11785                             }
11786                             
11787                             
11788                         ]
11789                     }
11790                 ]
11791                 
11792             }
11793         };
11794         var cal_rows = function() {
11795             
11796             var ret = []
11797             for (var r = 0; r < 6; r++) {
11798                 var row= {
11799                     tag : 'tr',
11800                     cls : 'fc-week',
11801                     cn : []
11802                 };
11803                 
11804                 for (var i =0; i < Date.dayNames.length; i++) {
11805                     var d = Date.dayNames[i];
11806                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
11807
11808                 }
11809                 row.cn[0].cls+=' fc-first';
11810                 row.cn[0].cn[0].style = 'min-height:90px';
11811                 row.cn[6].cls+=' fc-last';
11812                 ret.push(row);
11813                 
11814             }
11815             ret[0].cls += ' fc-first';
11816             ret[4].cls += ' fc-prev-last';
11817             ret[5].cls += ' fc-last';
11818             return ret;
11819             
11820         };
11821         
11822         var cal_table = {
11823             tag: 'table',
11824             cls: 'fc-border-separate',
11825             style : 'width:100%',
11826             cellspacing  : 0,
11827             cn : [
11828                 { 
11829                     tag: 'thead',
11830                     cn : [
11831                         { 
11832                             tag: 'tr',
11833                             cls : 'fc-first fc-last',
11834                             cn : cal_heads()
11835                         }
11836                     ]
11837                 },
11838                 { 
11839                     tag: 'tbody',
11840                     cn : cal_rows()
11841                 }
11842                   
11843             ]
11844         };
11845          
11846          var cfg = {
11847             cls : 'fc fc-ltr',
11848             cn : [
11849                 header,
11850                 {
11851                     cls : 'fc-content',
11852                     style : "position: relative;",
11853                     cn : [
11854                         {
11855                             cls : 'fc-view fc-view-month fc-grid',
11856                             style : 'position: relative',
11857                             unselectable : 'on',
11858                             cn : [
11859                                 {
11860                                     cls : 'fc-event-container',
11861                                     style : 'position:absolute;z-index:8;top:0;left:0;'
11862                                 },
11863                                 cal_table
11864                             ]
11865                         }
11866                     ]
11867     
11868                 }
11869            ] 
11870             
11871         };
11872         
11873          
11874         
11875         return cfg;
11876     },
11877     
11878     
11879     initEvents : function()
11880     {
11881         if(!this.store){
11882             throw "can not find store for calendar";
11883         }
11884         
11885         var mark = {
11886             tag: "div",
11887             cls:"x-dlg-mask",
11888             style: "text-align:center",
11889             cn: [
11890                 {
11891                     tag: "div",
11892                     style: "background-color:white;width:50%;margin:250 auto",
11893                     cn: [
11894                         {
11895                             tag: "img",
11896                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
11897                         },
11898                         {
11899                             tag: "span",
11900                             html: "Loading"
11901                         }
11902                         
11903                     ]
11904                 }
11905             ]
11906         }
11907         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
11908         
11909         var size = this.el.select('.fc-content', true).first().getSize();
11910         this.maskEl.setSize(size.width, size.height);
11911         this.maskEl.enableDisplayMode("block");
11912         if(!this.loadMask){
11913             this.maskEl.hide();
11914         }
11915         
11916         this.store = Roo.factory(this.store, Roo.data);
11917         this.store.on('load', this.onLoad, this);
11918         this.store.on('beforeload', this.onBeforeLoad, this);
11919         
11920         this.resize();
11921         
11922         this.cells = this.el.select('.fc-day',true);
11923         //Roo.log(this.cells);
11924         this.textNodes = this.el.query('.fc-day-number');
11925         this.cells.addClassOnOver('fc-state-hover');
11926         
11927         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
11928         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
11929         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
11930         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
11931         
11932         this.on('monthchange', this.onMonthChange, this);
11933         
11934         this.update(new Date().clearTime());
11935     },
11936     
11937     resize : function() {
11938         var sz  = this.el.getSize();
11939         
11940         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
11941         this.el.select('.fc-day-content div',true).setHeight(34);
11942     },
11943     
11944     
11945     // private
11946     showPrevMonth : function(e){
11947         this.update(this.activeDate.add("mo", -1));
11948     },
11949     showToday : function(e){
11950         this.update(new Date().clearTime());
11951     },
11952     // private
11953     showNextMonth : function(e){
11954         this.update(this.activeDate.add("mo", 1));
11955     },
11956
11957     // private
11958     showPrevYear : function(){
11959         this.update(this.activeDate.add("y", -1));
11960     },
11961
11962     // private
11963     showNextYear : function(){
11964         this.update(this.activeDate.add("y", 1));
11965     },
11966
11967     
11968    // private
11969     update : function(date)
11970     {
11971         var vd = this.activeDate;
11972         this.activeDate = date;
11973 //        if(vd && this.el){
11974 //            var t = date.getTime();
11975 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
11976 //                Roo.log('using add remove');
11977 //                
11978 //                this.fireEvent('monthchange', this, date);
11979 //                
11980 //                this.cells.removeClass("fc-state-highlight");
11981 //                this.cells.each(function(c){
11982 //                   if(c.dateValue == t){
11983 //                       c.addClass("fc-state-highlight");
11984 //                       setTimeout(function(){
11985 //                            try{c.dom.firstChild.focus();}catch(e){}
11986 //                       }, 50);
11987 //                       return false;
11988 //                   }
11989 //                   return true;
11990 //                });
11991 //                return;
11992 //            }
11993 //        }
11994         
11995         var days = date.getDaysInMonth();
11996         
11997         var firstOfMonth = date.getFirstDateOfMonth();
11998         var startingPos = firstOfMonth.getDay()-this.startDay;
11999         
12000         if(startingPos < this.startDay){
12001             startingPos += 7;
12002         }
12003         
12004         var pm = date.add(Date.MONTH, -1);
12005         var prevStart = pm.getDaysInMonth()-startingPos;
12006 //        
12007         this.cells = this.el.select('.fc-day',true);
12008         this.textNodes = this.el.query('.fc-day-number');
12009         this.cells.addClassOnOver('fc-state-hover');
12010         
12011         var cells = this.cells.elements;
12012         var textEls = this.textNodes;
12013         
12014         Roo.each(cells, function(cell){
12015             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
12016         });
12017         
12018         days += startingPos;
12019
12020         // convert everything to numbers so it's fast
12021         var day = 86400000;
12022         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
12023         //Roo.log(d);
12024         //Roo.log(pm);
12025         //Roo.log(prevStart);
12026         
12027         var today = new Date().clearTime().getTime();
12028         var sel = date.clearTime().getTime();
12029         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
12030         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
12031         var ddMatch = this.disabledDatesRE;
12032         var ddText = this.disabledDatesText;
12033         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
12034         var ddaysText = this.disabledDaysText;
12035         var format = this.format;
12036         
12037         var setCellClass = function(cal, cell){
12038             cell.row = 0;
12039             cell.events = [];
12040             cell.more = [];
12041             //Roo.log('set Cell Class');
12042             cell.title = "";
12043             var t = d.getTime();
12044             
12045             //Roo.log(d);
12046             
12047             cell.dateValue = t;
12048             if(t == today){
12049                 cell.className += " fc-today";
12050                 cell.className += " fc-state-highlight";
12051                 cell.title = cal.todayText;
12052             }
12053             if(t == sel){
12054                 // disable highlight in other month..
12055                 //cell.className += " fc-state-highlight";
12056                 
12057             }
12058             // disabling
12059             if(t < min) {
12060                 cell.className = " fc-state-disabled";
12061                 cell.title = cal.minText;
12062                 return;
12063             }
12064             if(t > max) {
12065                 cell.className = " fc-state-disabled";
12066                 cell.title = cal.maxText;
12067                 return;
12068             }
12069             if(ddays){
12070                 if(ddays.indexOf(d.getDay()) != -1){
12071                     cell.title = ddaysText;
12072                     cell.className = " fc-state-disabled";
12073                 }
12074             }
12075             if(ddMatch && format){
12076                 var fvalue = d.dateFormat(format);
12077                 if(ddMatch.test(fvalue)){
12078                     cell.title = ddText.replace("%0", fvalue);
12079                     cell.className = " fc-state-disabled";
12080                 }
12081             }
12082             
12083             if (!cell.initialClassName) {
12084                 cell.initialClassName = cell.dom.className;
12085             }
12086             
12087             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
12088         };
12089
12090         var i = 0;
12091         
12092         for(; i < startingPos; i++) {
12093             textEls[i].innerHTML = (++prevStart);
12094             d.setDate(d.getDate()+1);
12095             
12096             cells[i].className = "fc-past fc-other-month";
12097             setCellClass(this, cells[i]);
12098         }
12099         
12100         var intDay = 0;
12101         
12102         for(; i < days; i++){
12103             intDay = i - startingPos + 1;
12104             textEls[i].innerHTML = (intDay);
12105             d.setDate(d.getDate()+1);
12106             
12107             cells[i].className = ''; // "x-date-active";
12108             setCellClass(this, cells[i]);
12109         }
12110         var extraDays = 0;
12111         
12112         for(; i < 42; i++) {
12113             textEls[i].innerHTML = (++extraDays);
12114             d.setDate(d.getDate()+1);
12115             
12116             cells[i].className = "fc-future fc-other-month";
12117             setCellClass(this, cells[i]);
12118         }
12119         
12120         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
12121         
12122         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
12123         
12124         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
12125         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
12126         
12127         if(totalRows != 6){
12128             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
12129             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
12130         }
12131         
12132         this.fireEvent('monthchange', this, date);
12133         
12134         
12135         /*
12136         if(!this.internalRender){
12137             var main = this.el.dom.firstChild;
12138             var w = main.offsetWidth;
12139             this.el.setWidth(w + this.el.getBorderWidth("lr"));
12140             Roo.fly(main).setWidth(w);
12141             this.internalRender = true;
12142             // opera does not respect the auto grow header center column
12143             // then, after it gets a width opera refuses to recalculate
12144             // without a second pass
12145             if(Roo.isOpera && !this.secondPass){
12146                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
12147                 this.secondPass = true;
12148                 this.update.defer(10, this, [date]);
12149             }
12150         }
12151         */
12152         
12153     },
12154     
12155     findCell : function(dt) {
12156         dt = dt.clearTime().getTime();
12157         var ret = false;
12158         this.cells.each(function(c){
12159             //Roo.log("check " +c.dateValue + '?=' + dt);
12160             if(c.dateValue == dt){
12161                 ret = c;
12162                 return false;
12163             }
12164             return true;
12165         });
12166         
12167         return ret;
12168     },
12169     
12170     findCells : function(ev) {
12171         var s = ev.start.clone().clearTime().getTime();
12172        // Roo.log(s);
12173         var e= ev.end.clone().clearTime().getTime();
12174        // Roo.log(e);
12175         var ret = [];
12176         this.cells.each(function(c){
12177              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
12178             
12179             if(c.dateValue > e){
12180                 return ;
12181             }
12182             if(c.dateValue < s){
12183                 return ;
12184             }
12185             ret.push(c);
12186         });
12187         
12188         return ret;    
12189     },
12190     
12191 //    findBestRow: function(cells)
12192 //    {
12193 //        var ret = 0;
12194 //        
12195 //        for (var i =0 ; i < cells.length;i++) {
12196 //            ret  = Math.max(cells[i].rows || 0,ret);
12197 //        }
12198 //        return ret;
12199 //        
12200 //    },
12201     
12202     
12203     addItem : function(ev)
12204     {
12205         // look for vertical location slot in
12206         var cells = this.findCells(ev);
12207         
12208 //        ev.row = this.findBestRow(cells);
12209         
12210         // work out the location.
12211         
12212         var crow = false;
12213         var rows = [];
12214         for(var i =0; i < cells.length; i++) {
12215             
12216             cells[i].row = cells[0].row;
12217             
12218             if(i == 0){
12219                 cells[i].row = cells[i].row + 1;
12220             }
12221             
12222             if (!crow) {
12223                 crow = {
12224                     start : cells[i],
12225                     end :  cells[i]
12226                 };
12227                 continue;
12228             }
12229             if (crow.start.getY() == cells[i].getY()) {
12230                 // on same row.
12231                 crow.end = cells[i];
12232                 continue;
12233             }
12234             // different row.
12235             rows.push(crow);
12236             crow = {
12237                 start: cells[i],
12238                 end : cells[i]
12239             };
12240             
12241         }
12242         
12243         rows.push(crow);
12244         ev.els = [];
12245         ev.rows = rows;
12246         ev.cells = cells;
12247         
12248         cells[0].events.push(ev);
12249         
12250         this.calevents.push(ev);
12251     },
12252     
12253     clearEvents: function() {
12254         
12255         if(!this.calevents){
12256             return;
12257         }
12258         
12259         Roo.each(this.cells.elements, function(c){
12260             c.row = 0;
12261             c.events = [];
12262             c.more = [];
12263         });
12264         
12265         Roo.each(this.calevents, function(e) {
12266             Roo.each(e.els, function(el) {
12267                 el.un('mouseenter' ,this.onEventEnter, this);
12268                 el.un('mouseleave' ,this.onEventLeave, this);
12269                 el.remove();
12270             },this);
12271         },this);
12272         
12273         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
12274             e.remove();
12275         });
12276         
12277     },
12278     
12279     renderEvents: function()
12280     {   
12281         var _this = this;
12282         
12283         this.cells.each(function(c) {
12284             
12285             if(c.row < 5){
12286                 return;
12287             }
12288             
12289             var ev = c.events;
12290             
12291             var r = 4;
12292             if(c.row != c.events.length){
12293                 r = 4 - (4 - (c.row - c.events.length));
12294             }
12295             
12296             c.events = ev.slice(0, r);
12297             c.more = ev.slice(r);
12298             
12299             if(c.more.length && c.more.length == 1){
12300                 c.events.push(c.more.pop());
12301             }
12302             
12303             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
12304             
12305         });
12306             
12307         this.cells.each(function(c) {
12308             
12309             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
12310             
12311             
12312             for (var e = 0; e < c.events.length; e++){
12313                 var ev = c.events[e];
12314                 var rows = ev.rows;
12315                 
12316                 for(var i = 0; i < rows.length; i++) {
12317                 
12318                     // how many rows should it span..
12319
12320                     var  cfg = {
12321                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
12322                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
12323
12324                         unselectable : "on",
12325                         cn : [
12326                             {
12327                                 cls: 'fc-event-inner',
12328                                 cn : [
12329     //                                {
12330     //                                  tag:'span',
12331     //                                  cls: 'fc-event-time',
12332     //                                  html : cells.length > 1 ? '' : ev.time
12333     //                                },
12334                                     {
12335                                       tag:'span',
12336                                       cls: 'fc-event-title',
12337                                       html : String.format('{0}', ev.title)
12338                                     }
12339
12340
12341                                 ]
12342                             },
12343                             {
12344                                 cls: 'ui-resizable-handle ui-resizable-e',
12345                                 html : '&nbsp;&nbsp;&nbsp'
12346                             }
12347
12348                         ]
12349                     };
12350
12351                     if (i == 0) {
12352                         cfg.cls += ' fc-event-start';
12353                     }
12354                     if ((i+1) == rows.length) {
12355                         cfg.cls += ' fc-event-end';
12356                     }
12357
12358                     var ctr = _this.el.select('.fc-event-container',true).first();
12359                     var cg = ctr.createChild(cfg);
12360
12361                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
12362                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
12363
12364                     var r = (c.more.length) ? 1 : 0;
12365                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
12366                     cg.setWidth(ebox.right - sbox.x -2);
12367
12368                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
12369                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
12370                     cg.on('click', _this.onEventClick, _this, ev);
12371
12372                     ev.els.push(cg);
12373                     
12374                 }
12375                 
12376             }
12377             
12378             
12379             if(c.more.length){
12380                 var  cfg = {
12381                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
12382                     style : 'position: absolute',
12383                     unselectable : "on",
12384                     cn : [
12385                         {
12386                             cls: 'fc-event-inner',
12387                             cn : [
12388                                 {
12389                                   tag:'span',
12390                                   cls: 'fc-event-title',
12391                                   html : 'More'
12392                                 }
12393
12394
12395                             ]
12396                         },
12397                         {
12398                             cls: 'ui-resizable-handle ui-resizable-e',
12399                             html : '&nbsp;&nbsp;&nbsp'
12400                         }
12401
12402                     ]
12403                 };
12404
12405                 var ctr = _this.el.select('.fc-event-container',true).first();
12406                 var cg = ctr.createChild(cfg);
12407
12408                 var sbox = c.select('.fc-day-content',true).first().getBox();
12409                 var ebox = c.select('.fc-day-content',true).first().getBox();
12410                 //Roo.log(cg);
12411                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
12412                 cg.setWidth(ebox.right - sbox.x -2);
12413
12414                 cg.on('click', _this.onMoreEventClick, _this, c.more);
12415                 
12416             }
12417             
12418         });
12419         
12420         
12421         
12422     },
12423     
12424     onEventEnter: function (e, el,event,d) {
12425         this.fireEvent('evententer', this, el, event);
12426     },
12427     
12428     onEventLeave: function (e, el,event,d) {
12429         this.fireEvent('eventleave', this, el, event);
12430     },
12431     
12432     onEventClick: function (e, el,event,d) {
12433         this.fireEvent('eventclick', this, el, event);
12434     },
12435     
12436     onMonthChange: function () {
12437         this.store.load();
12438     },
12439     
12440     onMoreEventClick: function(e, el, more)
12441     {
12442         var _this = this;
12443         
12444         this.calpopover.placement = 'right';
12445         this.calpopover.setTitle('More');
12446         
12447         this.calpopover.setContent('');
12448         
12449         var ctr = this.calpopover.el.select('.popover-content', true).first();
12450         
12451         Roo.each(more, function(m){
12452             var cfg = {
12453                 cls : 'fc-event-hori fc-event-draggable',
12454                 html : m.title
12455             }
12456             var cg = ctr.createChild(cfg);
12457             
12458             cg.on('click', _this.onEventClick, _this, m);
12459         });
12460         
12461         this.calpopover.show(el);
12462         
12463         
12464     },
12465     
12466     onLoad: function () 
12467     {   
12468         this.calevents = [];
12469         var cal = this;
12470         
12471         if(this.store.getCount() > 0){
12472             this.store.data.each(function(d){
12473                cal.addItem({
12474                     id : d.data.id,
12475                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
12476                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
12477                     time : d.data.start_time,
12478                     title : d.data.title,
12479                     description : d.data.description,
12480                     venue : d.data.venue
12481                 });
12482             });
12483         }
12484         
12485         this.renderEvents();
12486         
12487         if(this.calevents.length && this.loadMask){
12488             this.maskEl.hide();
12489         }
12490     },
12491     
12492     onBeforeLoad: function()
12493     {
12494         this.clearEvents();
12495         if(this.loadMask){
12496             this.maskEl.show();
12497         }
12498     }
12499 });
12500
12501  
12502  /*
12503  * - LGPL
12504  *
12505  * element
12506  * 
12507  */
12508
12509 /**
12510  * @class Roo.bootstrap.Popover
12511  * @extends Roo.bootstrap.Component
12512  * Bootstrap Popover class
12513  * @cfg {String} html contents of the popover   (or false to use children..)
12514  * @cfg {String} title of popover (or false to hide)
12515  * @cfg {String} placement how it is placed
12516  * @cfg {String} trigger click || hover (or false to trigger manually)
12517  * @cfg {String} over what (parent or false to trigger manually.)
12518  * 
12519  * @constructor
12520  * Create a new Popover
12521  * @param {Object} config The config object
12522  */
12523
12524 Roo.bootstrap.Popover = function(config){
12525     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
12526 };
12527
12528 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
12529     
12530     title: 'Fill in a title',
12531     html: false,
12532     
12533     placement : 'right',
12534     trigger : 'hover', // hover
12535     
12536     over: 'parent',
12537     
12538     can_build_overlaid : false,
12539     
12540     getChildContainer : function()
12541     {
12542         return this.el.select('.popover-content',true).first();
12543     },
12544     
12545     getAutoCreate : function(){
12546          Roo.log('make popover?');
12547         var cfg = {
12548            cls : 'popover roo-dynamic',
12549            style: 'display:block',
12550            cn : [
12551                 {
12552                     cls : 'arrow'
12553                 },
12554                 {
12555                     cls : 'popover-inner',
12556                     cn : [
12557                         {
12558                             tag: 'h3',
12559                             cls: 'popover-title',
12560                             html : this.title
12561                         },
12562                         {
12563                             cls : 'popover-content',
12564                             html : this.html
12565                         }
12566                     ]
12567                     
12568                 }
12569            ]
12570         };
12571         
12572         return cfg;
12573     },
12574     setTitle: function(str)
12575     {
12576         this.el.select('.popover-title',true).first().dom.innerHTML = str;
12577     },
12578     setContent: function(str)
12579     {
12580         this.el.select('.popover-content',true).first().dom.innerHTML = str;
12581     },
12582     // as it get's added to the bottom of the page.
12583     onRender : function(ct, position)
12584     {
12585         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
12586         if(!this.el){
12587             var cfg = Roo.apply({},  this.getAutoCreate());
12588             cfg.id = Roo.id();
12589             
12590             if (this.cls) {
12591                 cfg.cls += ' ' + this.cls;
12592             }
12593             if (this.style) {
12594                 cfg.style = this.style;
12595             }
12596             Roo.log("adding to ")
12597             this.el = Roo.get(document.body).createChild(cfg, position);
12598             Roo.log(this.el);
12599         }
12600         this.initEvents();
12601     },
12602     
12603     initEvents : function()
12604     {
12605         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
12606         this.el.enableDisplayMode('block');
12607         this.el.hide();
12608         if (this.over === false) {
12609             return; 
12610         }
12611         if (this.triggers === false) {
12612             return;
12613         }
12614         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12615         var triggers = this.trigger ? this.trigger.split(' ') : [];
12616         Roo.each(triggers, function(trigger) {
12617         
12618             if (trigger == 'click') {
12619                 on_el.on('click', this.toggle, this);
12620             } else if (trigger != 'manual') {
12621                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
12622                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
12623       
12624                 on_el.on(eventIn  ,this.enter, this);
12625                 on_el.on(eventOut, this.leave, this);
12626             }
12627         }, this);
12628         
12629     },
12630     
12631     
12632     // private
12633     timeout : null,
12634     hoverState : null,
12635     
12636     toggle : function () {
12637         this.hoverState == 'in' ? this.leave() : this.enter();
12638     },
12639     
12640     enter : function () {
12641        
12642     
12643         clearTimeout(this.timeout);
12644     
12645         this.hoverState = 'in'
12646     
12647         if (!this.delay || !this.delay.show) {
12648             this.show();
12649             return 
12650         }
12651         var _t = this;
12652         this.timeout = setTimeout(function () {
12653             if (_t.hoverState == 'in') {
12654                 _t.show();
12655             }
12656         }, this.delay.show)
12657     },
12658     leave : function() {
12659         clearTimeout(this.timeout);
12660     
12661         this.hoverState = 'out'
12662     
12663         if (!this.delay || !this.delay.hide) {
12664             this.hide();
12665             return 
12666         }
12667         var _t = this;
12668         this.timeout = setTimeout(function () {
12669             if (_t.hoverState == 'out') {
12670                 _t.hide();
12671             }
12672         }, this.delay.hide)
12673     },
12674     
12675     show : function (on_el)
12676     {
12677         if (!on_el) {
12678             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12679         }
12680         // set content.
12681         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
12682         if (this.html !== false) {
12683             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
12684         }
12685         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
12686         if (!this.title.length) {
12687             this.el.select('.popover-title',true).hide();
12688         }
12689         
12690         var placement = typeof this.placement == 'function' ?
12691             this.placement.call(this, this.el, on_el) :
12692             this.placement;
12693             
12694         var autoToken = /\s?auto?\s?/i;
12695         var autoPlace = autoToken.test(placement);
12696         if (autoPlace) {
12697             placement = placement.replace(autoToken, '') || 'top';
12698         }
12699         
12700         //this.el.detach()
12701         //this.el.setXY([0,0]);
12702         this.el.show();
12703         this.el.dom.style.display='block';
12704         this.el.addClass(placement);
12705         
12706         //this.el.appendTo(on_el);
12707         
12708         var p = this.getPosition();
12709         var box = this.el.getBox();
12710         
12711         if (autoPlace) {
12712             // fixme..
12713         }
12714         var align = Roo.bootstrap.Popover.alignment[placement]
12715         this.el.alignTo(on_el, align[0],align[1]);
12716         //var arrow = this.el.select('.arrow',true).first();
12717         //arrow.set(align[2], 
12718         
12719         this.el.addClass('in');
12720         this.hoverState = null;
12721         
12722         if (this.el.hasClass('fade')) {
12723             // fade it?
12724         }
12725         
12726     },
12727     hide : function()
12728     {
12729         this.el.setXY([0,0]);
12730         this.el.removeClass('in');
12731         this.el.hide();
12732         
12733     }
12734     
12735 });
12736
12737 Roo.bootstrap.Popover.alignment = {
12738     'left' : ['r-l', [-10,0], 'right'],
12739     'right' : ['l-r', [10,0], 'left'],
12740     'bottom' : ['t-b', [0,10], 'top'],
12741     'top' : [ 'b-t', [0,-10], 'bottom']
12742 };
12743
12744  /*
12745  * - LGPL
12746  *
12747  * Progress
12748  * 
12749  */
12750
12751 /**
12752  * @class Roo.bootstrap.Progress
12753  * @extends Roo.bootstrap.Component
12754  * Bootstrap Progress class
12755  * @cfg {Boolean} striped striped of the progress bar
12756  * @cfg {Boolean} active animated of the progress bar
12757  * 
12758  * 
12759  * @constructor
12760  * Create a new Progress
12761  * @param {Object} config The config object
12762  */
12763
12764 Roo.bootstrap.Progress = function(config){
12765     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
12766 };
12767
12768 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
12769     
12770     striped : false,
12771     active: false,
12772     
12773     getAutoCreate : function(){
12774         var cfg = {
12775             tag: 'div',
12776             cls: 'progress'
12777         };
12778         
12779         
12780         if(this.striped){
12781             cfg.cls += ' progress-striped';
12782         }
12783       
12784         if(this.active){
12785             cfg.cls += ' active';
12786         }
12787         
12788         
12789         return cfg;
12790     }
12791    
12792 });
12793
12794  
12795
12796  /*
12797  * - LGPL
12798  *
12799  * ProgressBar
12800  * 
12801  */
12802
12803 /**
12804  * @class Roo.bootstrap.ProgressBar
12805  * @extends Roo.bootstrap.Component
12806  * Bootstrap ProgressBar class
12807  * @cfg {Number} aria_valuenow aria-value now
12808  * @cfg {Number} aria_valuemin aria-value min
12809  * @cfg {Number} aria_valuemax aria-value max
12810  * @cfg {String} label label for the progress bar
12811  * @cfg {String} panel (success | info | warning | danger )
12812  * @cfg {String} role role of the progress bar
12813  * @cfg {String} sr_only text
12814  * 
12815  * 
12816  * @constructor
12817  * Create a new ProgressBar
12818  * @param {Object} config The config object
12819  */
12820
12821 Roo.bootstrap.ProgressBar = function(config){
12822     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
12823 };
12824
12825 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
12826     
12827     aria_valuenow : 0,
12828     aria_valuemin : 0,
12829     aria_valuemax : 100,
12830     label : false,
12831     panel : false,
12832     role : false,
12833     sr_only: false,
12834     
12835     getAutoCreate : function()
12836     {
12837         
12838         var cfg = {
12839             tag: 'div',
12840             cls: 'progress-bar',
12841             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
12842         };
12843         
12844         if(this.sr_only){
12845             cfg.cn = {
12846                 tag: 'span',
12847                 cls: 'sr-only',
12848                 html: this.sr_only
12849             }
12850         }
12851         
12852         if(this.role){
12853             cfg.role = this.role;
12854         }
12855         
12856         if(this.aria_valuenow){
12857             cfg['aria-valuenow'] = this.aria_valuenow;
12858         }
12859         
12860         if(this.aria_valuemin){
12861             cfg['aria-valuemin'] = this.aria_valuemin;
12862         }
12863         
12864         if(this.aria_valuemax){
12865             cfg['aria-valuemax'] = this.aria_valuemax;
12866         }
12867         
12868         if(this.label && !this.sr_only){
12869             cfg.html = this.label;
12870         }
12871         
12872         if(this.panel){
12873             cfg.cls += ' progress-bar-' + this.panel;
12874         }
12875         
12876         return cfg;
12877     },
12878     
12879     update : function(aria_valuenow)
12880     {
12881         this.aria_valuenow = aria_valuenow;
12882         
12883         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
12884     }
12885    
12886 });
12887
12888  
12889
12890  /*
12891  * - LGPL
12892  *
12893  * TabPanel
12894  * 
12895  */
12896
12897 /**
12898  * @class Roo.bootstrap.TabPanel
12899  * @extends Roo.bootstrap.Component
12900  * Bootstrap TabPanel class
12901  * @cfg {Boolean} active panel active
12902  * @cfg {String} html panel content
12903  * @cfg {String} tabId tab relate id
12904  * @cfg {String} navId The navbar which triggers show hide
12905  * 
12906  * 
12907  * @constructor
12908  * Create a new TabPanel
12909  * @param {Object} config The config object
12910  */
12911
12912 Roo.bootstrap.TabPanel = function(config){
12913     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
12914      this.addEvents({
12915         /**
12916              * @event changed
12917              * Fires when the active status changes
12918              * @param {Roo.bootstrap.TabPanel} this
12919              * @param {Boolean} state the new state
12920             
12921          */
12922         'changed': true
12923      });
12924 };
12925
12926 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
12927     
12928     active: false,
12929     html: false,
12930     tabId: false,
12931     navId : false,
12932     
12933     getAutoCreate : function(){
12934         var cfg = {
12935             tag: 'div',
12936             cls: 'tab-pane',
12937             html: this.html || ''
12938         };
12939         
12940         if(this.active){
12941             cfg.cls += ' active';
12942         }
12943         
12944         if(this.tabId){
12945             cfg.tabId = this.tabId;
12946         }
12947         
12948         return cfg;
12949     },
12950     onRender : function(ct, position)
12951     {
12952        // Roo.log("Call onRender: " + this.xtype);
12953         
12954         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
12955         
12956         if (this.navId && this.tabId) {
12957             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
12958             if (!item) {
12959                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
12960             } else {
12961                 item.on('changed', function(item, state) {
12962                     this.setActive(state);
12963                 }, this);
12964             }
12965         }
12966         
12967     },
12968     setActive: function(state)
12969     {
12970         Roo.log("panel - set active " + this.tabId + "=" + state);
12971         
12972         this.active = state;
12973         if (!state) {
12974             this.el.removeClass('active');
12975             
12976         } else  if (!this.el.hasClass('active')) {
12977             this.el.addClass('active');
12978         }
12979         this.fireEvent('changed', this, state);
12980     }
12981     
12982     
12983 });
12984  
12985
12986  
12987
12988  /*
12989  * - LGPL
12990  *
12991  * DateField
12992  * 
12993  */
12994
12995 /**
12996  * @class Roo.bootstrap.DateField
12997  * @extends Roo.bootstrap.Input
12998  * Bootstrap DateField class
12999  * @cfg {Number} weekStart default 0
13000  * @cfg {Number} weekStart default 0
13001  * @cfg {Number} viewMode default empty, (months|years)
13002  * @cfg {Number} minViewMode default empty, (months|years)
13003  * @cfg {Number} startDate default -Infinity
13004  * @cfg {Number} endDate default Infinity
13005  * @cfg {Boolean} todayHighlight default false
13006  * @cfg {Boolean} todayBtn default false
13007  * @cfg {Boolean} calendarWeeks default false
13008  * @cfg {Object} daysOfWeekDisabled default empty
13009  * 
13010  * @cfg {Boolean} keyboardNavigation default true
13011  * @cfg {String} language default en
13012  * 
13013  * @constructor
13014  * Create a new DateField
13015  * @param {Object} config The config object
13016  */
13017
13018 Roo.bootstrap.DateField = function(config){
13019     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
13020      this.addEvents({
13021             /**
13022              * @event show
13023              * Fires when this field show.
13024              * @param {Roo.bootstrap.DateField} this
13025              * @param {Mixed} date The date value
13026              */
13027             show : true,
13028             /**
13029              * @event show
13030              * Fires when this field hide.
13031              * @param {Roo.bootstrap.DateField} this
13032              * @param {Mixed} date The date value
13033              */
13034             hide : true,
13035             /**
13036              * @event select
13037              * Fires when select a date.
13038              * @param {Roo.bootstrap.DateField} this
13039              * @param {Mixed} date The date value
13040              */
13041             select : true
13042         });
13043 };
13044
13045 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
13046     
13047     /**
13048      * @cfg {String} format
13049      * The default date format string which can be overriden for localization support.  The format must be
13050      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
13051      */
13052     format : "m/d/y",
13053     /**
13054      * @cfg {String} altFormats
13055      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
13056      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
13057      */
13058     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
13059     
13060     weekStart : 0,
13061     
13062     viewMode : '',
13063     
13064     minViewMode : '',
13065     
13066     todayHighlight : false,
13067     
13068     todayBtn: false,
13069     
13070     language: 'en',
13071     
13072     keyboardNavigation: true,
13073     
13074     calendarWeeks: false,
13075     
13076     startDate: -Infinity,
13077     
13078     endDate: Infinity,
13079     
13080     daysOfWeekDisabled: [],
13081     
13082     _events: [],
13083     
13084     UTCDate: function()
13085     {
13086         return new Date(Date.UTC.apply(Date, arguments));
13087     },
13088     
13089     UTCToday: function()
13090     {
13091         var today = new Date();
13092         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
13093     },
13094     
13095     getDate: function() {
13096             var d = this.getUTCDate();
13097             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
13098     },
13099     
13100     getUTCDate: function() {
13101             return this.date;
13102     },
13103     
13104     setDate: function(d) {
13105             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
13106     },
13107     
13108     setUTCDate: function(d) {
13109             this.date = d;
13110             this.setValue(this.formatDate(this.date));
13111     },
13112         
13113     onRender: function(ct, position)
13114     {
13115         
13116         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
13117         
13118         this.language = this.language || 'en';
13119         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
13120         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
13121         
13122         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
13123         this.format = this.format || 'm/d/y';
13124         this.isInline = false;
13125         this.isInput = true;
13126         this.component = this.el.select('.add-on', true).first() || false;
13127         this.component = (this.component && this.component.length === 0) ? false : this.component;
13128         this.hasInput = this.component && this.inputEL().length;
13129         
13130         if (typeof(this.minViewMode === 'string')) {
13131             switch (this.minViewMode) {
13132                 case 'months':
13133                     this.minViewMode = 1;
13134                     break;
13135                 case 'years':
13136                     this.minViewMode = 2;
13137                     break;
13138                 default:
13139                     this.minViewMode = 0;
13140                     break;
13141             }
13142         }
13143         
13144         if (typeof(this.viewMode === 'string')) {
13145             switch (this.viewMode) {
13146                 case 'months':
13147                     this.viewMode = 1;
13148                     break;
13149                 case 'years':
13150                     this.viewMode = 2;
13151                     break;
13152                 default:
13153                     this.viewMode = 0;
13154                     break;
13155             }
13156         }
13157                 
13158         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
13159         
13160         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13161         
13162         this.picker().on('mousedown', this.onMousedown, this);
13163         this.picker().on('click', this.onClick, this);
13164         
13165         this.picker().addClass('datepicker-dropdown');
13166         
13167         this.startViewMode = this.viewMode;
13168         
13169         
13170         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
13171             if(!this.calendarWeeks){
13172                 v.remove();
13173                 return;
13174             };
13175             
13176             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
13177             v.attr('colspan', function(i, val){
13178                 return parseInt(val) + 1;
13179             });
13180         })
13181                         
13182         
13183         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
13184         
13185         this.setStartDate(this.startDate);
13186         this.setEndDate(this.endDate);
13187         
13188         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
13189         
13190         this.fillDow();
13191         this.fillMonths();
13192         this.update();
13193         this.showMode();
13194         
13195         if(this.isInline) {
13196             this.show();
13197         }
13198     },
13199     
13200     picker : function()
13201     {
13202         return this.el.select('.datepicker', true).first();
13203     },
13204     
13205     fillDow: function()
13206     {
13207         var dowCnt = this.weekStart;
13208         
13209         var dow = {
13210             tag: 'tr',
13211             cn: [
13212                 
13213             ]
13214         };
13215         
13216         if(this.calendarWeeks){
13217             dow.cn.push({
13218                 tag: 'th',
13219                 cls: 'cw',
13220                 html: '&nbsp;'
13221             })
13222         }
13223         
13224         while (dowCnt < this.weekStart + 7) {
13225             dow.cn.push({
13226                 tag: 'th',
13227                 cls: 'dow',
13228                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
13229             });
13230         }
13231         
13232         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
13233     },
13234     
13235     fillMonths: function()
13236     {    
13237         var i = 0
13238         var months = this.picker().select('>.datepicker-months td', true).first();
13239         
13240         months.dom.innerHTML = '';
13241         
13242         while (i < 12) {
13243             var month = {
13244                 tag: 'span',
13245                 cls: 'month',
13246                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
13247             }
13248             
13249             months.createChild(month);
13250         }
13251         
13252     },
13253     
13254     update: function(){
13255         
13256         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
13257         
13258         if (this.date < this.startDate) {
13259             this.viewDate = new Date(this.startDate);
13260         } else if (this.date > this.endDate) {
13261             this.viewDate = new Date(this.endDate);
13262         } else {
13263             this.viewDate = new Date(this.date);
13264         }
13265         
13266         this.fill();
13267     },
13268     
13269     fill: function() {
13270         var d = new Date(this.viewDate),
13271                 year = d.getUTCFullYear(),
13272                 month = d.getUTCMonth(),
13273                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
13274                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
13275                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
13276                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
13277                 currentDate = this.date && this.date.valueOf(),
13278                 today = this.UTCToday();
13279         
13280         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
13281         
13282 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
13283         
13284 //        this.picker.select('>tfoot th.today').
13285 //                                              .text(dates[this.language].today)
13286 //                                              .toggle(this.todayBtn !== false);
13287     
13288         this.updateNavArrows();
13289         this.fillMonths();
13290                                                 
13291         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
13292         
13293         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
13294          
13295         prevMonth.setUTCDate(day);
13296         
13297         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
13298         
13299         var nextMonth = new Date(prevMonth);
13300         
13301         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
13302         
13303         nextMonth = nextMonth.valueOf();
13304         
13305         var fillMonths = false;
13306         
13307         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
13308         
13309         while(prevMonth.valueOf() < nextMonth) {
13310             var clsName = '';
13311             
13312             if (prevMonth.getUTCDay() === this.weekStart) {
13313                 if(fillMonths){
13314                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
13315                 }
13316                     
13317                 fillMonths = {
13318                     tag: 'tr',
13319                     cn: []
13320                 };
13321                 
13322                 if(this.calendarWeeks){
13323                     // ISO 8601: First week contains first thursday.
13324                     // ISO also states week starts on Monday, but we can be more abstract here.
13325                     var
13326                     // Start of current week: based on weekstart/current date
13327                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
13328                     // Thursday of this week
13329                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
13330                     // First Thursday of year, year from thursday
13331                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
13332                     // Calendar week: ms between thursdays, div ms per day, div 7 days
13333                     calWeek =  (th - yth) / 864e5 / 7 + 1;
13334                     
13335                     fillMonths.cn.push({
13336                         tag: 'td',
13337                         cls: 'cw',
13338                         html: calWeek
13339                     });
13340                 }
13341             }
13342             
13343             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
13344                 clsName += ' old';
13345             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
13346                 clsName += ' new';
13347             }
13348             if (this.todayHighlight &&
13349                 prevMonth.getUTCFullYear() == today.getFullYear() &&
13350                 prevMonth.getUTCMonth() == today.getMonth() &&
13351                 prevMonth.getUTCDate() == today.getDate()) {
13352                 clsName += ' today';
13353             }
13354             
13355             if (currentDate && prevMonth.valueOf() === currentDate) {
13356                 clsName += ' active';
13357             }
13358             
13359             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
13360                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
13361                     clsName += ' disabled';
13362             }
13363             
13364             fillMonths.cn.push({
13365                 tag: 'td',
13366                 cls: 'day ' + clsName,
13367                 html: prevMonth.getDate()
13368             })
13369             
13370             prevMonth.setDate(prevMonth.getDate()+1);
13371         }
13372           
13373         var currentYear = this.date && this.date.getUTCFullYear();
13374         var currentMonth = this.date && this.date.getUTCMonth();
13375         
13376         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
13377         
13378         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
13379             v.removeClass('active');
13380             
13381             if(currentYear === year && k === currentMonth){
13382                 v.addClass('active');
13383             }
13384             
13385             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
13386                 v.addClass('disabled');
13387             }
13388             
13389         });
13390         
13391         
13392         year = parseInt(year/10, 10) * 10;
13393         
13394         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
13395         
13396         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
13397         
13398         year -= 1;
13399         for (var i = -1; i < 11; i++) {
13400             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
13401                 tag: 'span',
13402                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
13403                 html: year
13404             })
13405             
13406             year += 1;
13407         }
13408     },
13409     
13410     showMode: function(dir) {
13411         if (dir) {
13412             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
13413         }
13414         Roo.each(this.picker().select('>div',true).elements, function(v){
13415             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13416             v.hide();
13417         });
13418         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
13419     },
13420     
13421     place: function()
13422     {
13423         if(this.isInline) return;
13424         
13425         this.picker().removeClass(['bottom', 'top']);
13426         
13427         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13428             /*
13429              * place to the top of element!
13430              *
13431              */
13432             
13433             this.picker().addClass('top');
13434             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13435             
13436             return;
13437         }
13438         
13439         this.picker().addClass('bottom');
13440         
13441         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13442     },
13443     
13444     parseDate : function(value){
13445         if(!value || value instanceof Date){
13446             return value;
13447         }
13448         var v = Date.parseDate(value, this.format);
13449         if (!v && this.useIso) {
13450             v = Date.parseDate(value, 'Y-m-d');
13451         }
13452         if(!v && this.altFormats){
13453             if(!this.altFormatsArray){
13454                 this.altFormatsArray = this.altFormats.split("|");
13455             }
13456             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
13457                 v = Date.parseDate(value, this.altFormatsArray[i]);
13458             }
13459         }
13460         return v;
13461     },
13462     
13463     formatDate : function(date, fmt){
13464         return (!date || !(date instanceof Date)) ?
13465         date : date.dateFormat(fmt || this.format);
13466     },
13467     
13468     onFocus : function()
13469     {
13470         Roo.bootstrap.DateField.superclass.onFocus.call(this);
13471         this.show();
13472     },
13473     
13474     onBlur : function()
13475     {
13476         Roo.bootstrap.DateField.superclass.onBlur.call(this);
13477         this.hide();
13478     },
13479     
13480     show : function()
13481     {
13482         this.picker().show();
13483         this.update();
13484         this.place();
13485         
13486         this.fireEvent('show', this, this.date);
13487     },
13488     
13489     hide : function()
13490     {
13491         if(this.isInline) return;
13492         this.picker().hide();
13493         this.viewMode = this.startViewMode;
13494         this.showMode();
13495         
13496         this.fireEvent('hide', this, this.date);
13497         
13498     },
13499     
13500     onMousedown: function(e){
13501         e.stopPropagation();
13502         e.preventDefault();
13503     },
13504     
13505     keyup: function(e){
13506         Roo.bootstrap.DateField.superclass.keyup.call(this);
13507         this.update();
13508         
13509     },
13510
13511     setValue: function(v){
13512         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
13513         
13514         this.fireEvent('select', this, this.date);
13515         
13516     },
13517     
13518     fireKey: function(e){
13519         if (!this.picker().isVisible()){
13520             if (e.keyCode == 27) // allow escape to hide and re-show picker
13521                 this.show();
13522             return;
13523         }
13524         var dateChanged = false,
13525         dir, day, month,
13526         newDate, newViewDate;
13527         switch(e.keyCode){
13528             case 27: // escape
13529                 this.hide();
13530                 e.preventDefault();
13531                 break;
13532             case 37: // left
13533             case 39: // right
13534                 if (!this.keyboardNavigation) break;
13535                 dir = e.keyCode == 37 ? -1 : 1;
13536                 
13537                 if (e.ctrlKey){
13538                     newDate = this.moveYear(this.date, dir);
13539                     newViewDate = this.moveYear(this.viewDate, dir);
13540                 } else if (e.shiftKey){
13541                     newDate = this.moveMonth(this.date, dir);
13542                     newViewDate = this.moveMonth(this.viewDate, dir);
13543                 } else {
13544                     newDate = new Date(this.date);
13545                     newDate.setUTCDate(this.date.getUTCDate() + dir);
13546                     newViewDate = new Date(this.viewDate);
13547                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
13548                 }
13549                 if (this.dateWithinRange(newDate)){
13550                     this.date = newDate;
13551                     this.viewDate = newViewDate;
13552                     this.setValue(this.formatDate(this.date));
13553                     this.update();
13554                     e.preventDefault();
13555                     dateChanged = true;
13556                 }
13557                 break;
13558             case 38: // up
13559             case 40: // down
13560                 if (!this.keyboardNavigation) break;
13561                 dir = e.keyCode == 38 ? -1 : 1;
13562                 if (e.ctrlKey){
13563                     newDate = this.moveYear(this.date, dir);
13564                     newViewDate = this.moveYear(this.viewDate, dir);
13565                 } else if (e.shiftKey){
13566                     newDate = this.moveMonth(this.date, dir);
13567                     newViewDate = this.moveMonth(this.viewDate, dir);
13568                 } else {
13569                     newDate = new Date(this.date);
13570                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
13571                     newViewDate = new Date(this.viewDate);
13572                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
13573                 }
13574                 if (this.dateWithinRange(newDate)){
13575                     this.date = newDate;
13576                     this.viewDate = newViewDate;
13577                     this.setValue(this.formatDate(this.date));
13578                     this.update();
13579                     e.preventDefault();
13580                     dateChanged = true;
13581                 }
13582                 break;
13583             case 13: // enter
13584                 this.setValue(this.formatDate(this.date));
13585                 this.hide();
13586                 e.preventDefault();
13587                 break;
13588             case 9: // tab
13589                 this.setValue(this.formatDate(this.date));
13590                 this.hide();
13591                 break;
13592         }
13593     },
13594     
13595     
13596     onClick: function(e) {
13597         e.stopPropagation();
13598         e.preventDefault();
13599         
13600         var target = e.getTarget();
13601         
13602         if(target.nodeName.toLowerCase() === 'i'){
13603             target = Roo.get(target).dom.parentNode;
13604         }
13605         
13606         var nodeName = target.nodeName;
13607         var className = target.className;
13608         var html = target.innerHTML;
13609         
13610         switch(nodeName.toLowerCase()) {
13611             case 'th':
13612                 switch(className) {
13613                     case 'switch':
13614                         this.showMode(1);
13615                         break;
13616                     case 'prev':
13617                     case 'next':
13618                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
13619                         switch(this.viewMode){
13620                                 case 0:
13621                                         this.viewDate = this.moveMonth(this.viewDate, dir);
13622                                         break;
13623                                 case 1:
13624                                 case 2:
13625                                         this.viewDate = this.moveYear(this.viewDate, dir);
13626                                         break;
13627                         }
13628                         this.fill();
13629                         break;
13630                     case 'today':
13631                         var date = new Date();
13632                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
13633                         this.fill()
13634                         this.setValue(this.formatDate(this.date));
13635                         this.hide();
13636                         break;
13637                 }
13638                 break;
13639             case 'span':
13640                 if (className.indexOf('disabled') === -1) {
13641                     this.viewDate.setUTCDate(1);
13642                     if (className.indexOf('month') !== -1) {
13643                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
13644                     } else {
13645                         var year = parseInt(html, 10) || 0;
13646                         this.viewDate.setUTCFullYear(year);
13647                         
13648                     }
13649                     this.showMode(-1);
13650                     this.fill();
13651                 }
13652                 break;
13653                 
13654             case 'td':
13655                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
13656                     var day = parseInt(html, 10) || 1;
13657                     var year = this.viewDate.getUTCFullYear(),
13658                         month = this.viewDate.getUTCMonth();
13659
13660                     if (className.indexOf('old') !== -1) {
13661                         if(month === 0 ){
13662                             month = 11;
13663                             year -= 1;
13664                         }else{
13665                             month -= 1;
13666                         }
13667                     } else if (className.indexOf('new') !== -1) {
13668                         if (month == 11) {
13669                             month = 0;
13670                             year += 1;
13671                         } else {
13672                             month += 1;
13673                         }
13674                     }
13675                     this.date = this.UTCDate(year, month, day,0,0,0,0);
13676                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
13677                     this.fill();
13678                     this.setValue(this.formatDate(this.date));
13679                     this.hide();
13680                 }
13681                 break;
13682         }
13683     },
13684     
13685     setStartDate: function(startDate){
13686         this.startDate = startDate || -Infinity;
13687         if (this.startDate !== -Infinity) {
13688             this.startDate = this.parseDate(this.startDate);
13689         }
13690         this.update();
13691         this.updateNavArrows();
13692     },
13693
13694     setEndDate: function(endDate){
13695         this.endDate = endDate || Infinity;
13696         if (this.endDate !== Infinity) {
13697             this.endDate = this.parseDate(this.endDate);
13698         }
13699         this.update();
13700         this.updateNavArrows();
13701     },
13702     
13703     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
13704         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
13705         if (typeof(this.daysOfWeekDisabled) !== 'object') {
13706             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
13707         }
13708         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
13709             return parseInt(d, 10);
13710         });
13711         this.update();
13712         this.updateNavArrows();
13713     },
13714     
13715     updateNavArrows: function() {
13716         var d = new Date(this.viewDate),
13717         year = d.getUTCFullYear(),
13718         month = d.getUTCMonth();
13719         
13720         Roo.each(this.picker().select('.prev', true).elements, function(v){
13721             v.show();
13722             switch (this.viewMode) {
13723                 case 0:
13724
13725                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
13726                         v.hide();
13727                     }
13728                     break;
13729                 case 1:
13730                 case 2:
13731                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
13732                         v.hide();
13733                     }
13734                     break;
13735             }
13736         });
13737         
13738         Roo.each(this.picker().select('.next', true).elements, function(v){
13739             v.show();
13740             switch (this.viewMode) {
13741                 case 0:
13742
13743                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
13744                         v.hide();
13745                     }
13746                     break;
13747                 case 1:
13748                 case 2:
13749                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
13750                         v.hide();
13751                     }
13752                     break;
13753             }
13754         })
13755     },
13756     
13757     moveMonth: function(date, dir){
13758         if (!dir) return date;
13759         var new_date = new Date(date.valueOf()),
13760         day = new_date.getUTCDate(),
13761         month = new_date.getUTCMonth(),
13762         mag = Math.abs(dir),
13763         new_month, test;
13764         dir = dir > 0 ? 1 : -1;
13765         if (mag == 1){
13766             test = dir == -1
13767             // If going back one month, make sure month is not current month
13768             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
13769             ? function(){
13770                 return new_date.getUTCMonth() == month;
13771             }
13772             // If going forward one month, make sure month is as expected
13773             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
13774             : function(){
13775                 return new_date.getUTCMonth() != new_month;
13776             };
13777             new_month = month + dir;
13778             new_date.setUTCMonth(new_month);
13779             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
13780             if (new_month < 0 || new_month > 11)
13781                 new_month = (new_month + 12) % 12;
13782         } else {
13783             // For magnitudes >1, move one month at a time...
13784             for (var i=0; i<mag; i++)
13785                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
13786                 new_date = this.moveMonth(new_date, dir);
13787             // ...then reset the day, keeping it in the new month
13788             new_month = new_date.getUTCMonth();
13789             new_date.setUTCDate(day);
13790             test = function(){
13791                 return new_month != new_date.getUTCMonth();
13792             };
13793         }
13794         // Common date-resetting loop -- if date is beyond end of month, make it
13795         // end of month
13796         while (test()){
13797             new_date.setUTCDate(--day);
13798             new_date.setUTCMonth(new_month);
13799         }
13800         return new_date;
13801     },
13802
13803     moveYear: function(date, dir){
13804         return this.moveMonth(date, dir*12);
13805     },
13806
13807     dateWithinRange: function(date){
13808         return date >= this.startDate && date <= this.endDate;
13809     },
13810
13811     
13812     remove: function() {
13813         this.picker().remove();
13814     }
13815    
13816 });
13817
13818 Roo.apply(Roo.bootstrap.DateField,  {
13819     
13820     head : {
13821         tag: 'thead',
13822         cn: [
13823         {
13824             tag: 'tr',
13825             cn: [
13826             {
13827                 tag: 'th',
13828                 cls: 'prev',
13829                 html: '<i class="icon-arrow-left"/>'
13830             },
13831             {
13832                 tag: 'th',
13833                 cls: 'switch',
13834                 colspan: '5'
13835             },
13836             {
13837                 tag: 'th',
13838                 cls: 'next',
13839                 html: '<i class="icon-arrow-right"/>'
13840             }
13841
13842             ]
13843         }
13844         ]
13845     },
13846     
13847     content : {
13848         tag: 'tbody',
13849         cn: [
13850         {
13851             tag: 'tr',
13852             cn: [
13853             {
13854                 tag: 'td',
13855                 colspan: '7'
13856             }
13857             ]
13858         }
13859         ]
13860     },
13861     
13862     footer : {
13863         tag: 'tfoot',
13864         cn: [
13865         {
13866             tag: 'tr',
13867             cn: [
13868             {
13869                 tag: 'th',
13870                 colspan: '7',
13871                 cls: 'today'
13872             }
13873                     
13874             ]
13875         }
13876         ]
13877     },
13878     
13879     dates:{
13880         en: {
13881             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
13882             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
13883             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
13884             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
13885             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
13886             today: "Today"
13887         }
13888     },
13889     
13890     modes: [
13891     {
13892         clsName: 'days',
13893         navFnc: 'Month',
13894         navStep: 1
13895     },
13896     {
13897         clsName: 'months',
13898         navFnc: 'FullYear',
13899         navStep: 1
13900     },
13901     {
13902         clsName: 'years',
13903         navFnc: 'FullYear',
13904         navStep: 10
13905     }]
13906 });
13907
13908 Roo.apply(Roo.bootstrap.DateField,  {
13909   
13910     template : {
13911         tag: 'div',
13912         cls: 'datepicker dropdown-menu',
13913         cn: [
13914         {
13915             tag: 'div',
13916             cls: 'datepicker-days',
13917             cn: [
13918             {
13919                 tag: 'table',
13920                 cls: 'table-condensed',
13921                 cn:[
13922                 Roo.bootstrap.DateField.head,
13923                 {
13924                     tag: 'tbody'
13925                 },
13926                 Roo.bootstrap.DateField.footer
13927                 ]
13928             }
13929             ]
13930         },
13931         {
13932             tag: 'div',
13933             cls: 'datepicker-months',
13934             cn: [
13935             {
13936                 tag: 'table',
13937                 cls: 'table-condensed',
13938                 cn:[
13939                 Roo.bootstrap.DateField.head,
13940                 Roo.bootstrap.DateField.content,
13941                 Roo.bootstrap.DateField.footer
13942                 ]
13943             }
13944             ]
13945         },
13946         {
13947             tag: 'div',
13948             cls: 'datepicker-years',
13949             cn: [
13950             {
13951                 tag: 'table',
13952                 cls: 'table-condensed',
13953                 cn:[
13954                 Roo.bootstrap.DateField.head,
13955                 Roo.bootstrap.DateField.content,
13956                 Roo.bootstrap.DateField.footer
13957                 ]
13958             }
13959             ]
13960         }
13961         ]
13962     }
13963 });
13964
13965  
13966
13967  /*
13968  * - LGPL
13969  *
13970  * TimeField
13971  * 
13972  */
13973
13974 /**
13975  * @class Roo.bootstrap.TimeField
13976  * @extends Roo.bootstrap.Input
13977  * Bootstrap DateField class
13978  * 
13979  * 
13980  * @constructor
13981  * Create a new TimeField
13982  * @param {Object} config The config object
13983  */
13984
13985 Roo.bootstrap.TimeField = function(config){
13986     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
13987     this.addEvents({
13988             /**
13989              * @event show
13990              * Fires when this field show.
13991              * @param {Roo.bootstrap.DateField} this
13992              * @param {Mixed} date The date value
13993              */
13994             show : true,
13995             /**
13996              * @event show
13997              * Fires when this field hide.
13998              * @param {Roo.bootstrap.DateField} this
13999              * @param {Mixed} date The date value
14000              */
14001             hide : true,
14002             /**
14003              * @event select
14004              * Fires when select a date.
14005              * @param {Roo.bootstrap.DateField} this
14006              * @param {Mixed} date The date value
14007              */
14008             select : true
14009         });
14010 };
14011
14012 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
14013     
14014     /**
14015      * @cfg {String} format
14016      * The default time format string which can be overriden for localization support.  The format must be
14017      * valid according to {@link Date#parseDate} (defaults to 'H:i').
14018      */
14019     format : "H:i",
14020        
14021     onRender: function(ct, position)
14022     {
14023         
14024         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
14025                 
14026         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
14027         
14028         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14029         
14030         this.pop = this.picker().select('>.datepicker-time',true).first();
14031         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
14032         
14033         this.picker().on('mousedown', this.onMousedown, this);
14034         this.picker().on('click', this.onClick, this);
14035         
14036         this.picker().addClass('datepicker-dropdown');
14037     
14038         this.fillTime();
14039         this.update();
14040             
14041         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
14042         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
14043         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
14044         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
14045         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
14046         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
14047
14048     },
14049     
14050     fireKey: function(e){
14051         if (!this.picker().isVisible()){
14052             if (e.keyCode == 27) // allow escape to hide and re-show picker
14053                 this.show();
14054             return;
14055         }
14056
14057         e.preventDefault();
14058         
14059         switch(e.keyCode){
14060             case 27: // escape
14061                 this.hide();
14062                 break;
14063             case 37: // left
14064             case 39: // right
14065                 this.onTogglePeriod();
14066                 break;
14067             case 38: // up
14068                 this.onIncrementMinutes();
14069                 break;
14070             case 40: // down
14071                 this.onDecrementMinutes();
14072                 break;
14073             case 13: // enter
14074             case 9: // tab
14075                 this.setTime();
14076                 break;
14077         }
14078     },
14079     
14080     onClick: function(e) {
14081         e.stopPropagation();
14082         e.preventDefault();
14083     },
14084     
14085     picker : function()
14086     {
14087         return this.el.select('.datepicker', true).first();
14088     },
14089     
14090     fillTime: function()
14091     {    
14092         var time = this.pop.select('tbody', true).first();
14093         
14094         time.dom.innerHTML = '';
14095         
14096         time.createChild({
14097             tag: 'tr',
14098             cn: [
14099                 {
14100                     tag: 'td',
14101                     cn: [
14102                         {
14103                             tag: 'a',
14104                             href: '#',
14105                             cls: 'btn',
14106                             cn: [
14107                                 {
14108                                     tag: 'span',
14109                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
14110                                 }
14111                             ]
14112                         } 
14113                     ]
14114                 },
14115                 {
14116                     tag: 'td',
14117                     cls: 'separator'
14118                 },
14119                 {
14120                     tag: 'td',
14121                     cn: [
14122                         {
14123                             tag: 'a',
14124                             href: '#',
14125                             cls: 'btn',
14126                             cn: [
14127                                 {
14128                                     tag: 'span',
14129                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
14130                                 }
14131                             ]
14132                         }
14133                     ]
14134                 },
14135                 {
14136                     tag: 'td',
14137                     cls: 'separator'
14138                 }
14139             ]
14140         });
14141         
14142         time.createChild({
14143             tag: 'tr',
14144             cn: [
14145                 {
14146                     tag: 'td',
14147                     cn: [
14148                         {
14149                             tag: 'span',
14150                             cls: 'timepicker-hour',
14151                             html: '00'
14152                         }  
14153                     ]
14154                 },
14155                 {
14156                     tag: 'td',
14157                     cls: 'separator',
14158                     html: ':'
14159                 },
14160                 {
14161                     tag: 'td',
14162                     cn: [
14163                         {
14164                             tag: 'span',
14165                             cls: 'timepicker-minute',
14166                             html: '00'
14167                         }  
14168                     ]
14169                 },
14170                 {
14171                     tag: 'td',
14172                     cls: 'separator'
14173                 },
14174                 {
14175                     tag: 'td',
14176                     cn: [
14177                         {
14178                             tag: 'button',
14179                             type: 'button',
14180                             cls: 'btn btn-primary period',
14181                             html: 'AM'
14182                             
14183                         }
14184                     ]
14185                 }
14186             ]
14187         });
14188         
14189         time.createChild({
14190             tag: 'tr',
14191             cn: [
14192                 {
14193                     tag: 'td',
14194                     cn: [
14195                         {
14196                             tag: 'a',
14197                             href: '#',
14198                             cls: 'btn',
14199                             cn: [
14200                                 {
14201                                     tag: 'span',
14202                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
14203                                 }
14204                             ]
14205                         }
14206                     ]
14207                 },
14208                 {
14209                     tag: 'td',
14210                     cls: 'separator'
14211                 },
14212                 {
14213                     tag: 'td',
14214                     cn: [
14215                         {
14216                             tag: 'a',
14217                             href: '#',
14218                             cls: 'btn',
14219                             cn: [
14220                                 {
14221                                     tag: 'span',
14222                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
14223                                 }
14224                             ]
14225                         }
14226                     ]
14227                 },
14228                 {
14229                     tag: 'td',
14230                     cls: 'separator'
14231                 }
14232             ]
14233         });
14234         
14235     },
14236     
14237     update: function()
14238     {
14239         
14240         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
14241         
14242         this.fill();
14243     },
14244     
14245     fill: function() 
14246     {
14247         var hours = this.time.getHours();
14248         var minutes = this.time.getMinutes();
14249         var period = 'AM';
14250         
14251         if(hours > 11){
14252             period = 'PM';
14253         }
14254         
14255         if(hours == 0){
14256             hours = 12;
14257         }
14258         
14259         
14260         if(hours > 12){
14261             hours = hours - 12;
14262         }
14263         
14264         if(hours < 10){
14265             hours = '0' + hours;
14266         }
14267         
14268         if(minutes < 10){
14269             minutes = '0' + minutes;
14270         }
14271         
14272         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
14273         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
14274         this.pop.select('button', true).first().dom.innerHTML = period;
14275         
14276     },
14277     
14278     place: function()
14279     {   
14280         this.picker().removeClass(['bottom', 'top']);
14281         
14282         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14283             /*
14284              * place to the top of element!
14285              *
14286              */
14287             
14288             this.picker().addClass('top');
14289             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14290             
14291             return;
14292         }
14293         
14294         this.picker().addClass('bottom');
14295         
14296         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14297     },
14298   
14299     onFocus : function()
14300     {
14301         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
14302         this.show();
14303     },
14304     
14305     onBlur : function()
14306     {
14307         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
14308         this.hide();
14309     },
14310     
14311     show : function()
14312     {
14313         this.picker().show();
14314         this.pop.show();
14315         this.update();
14316         this.place();
14317         
14318         this.fireEvent('show', this, this.date);
14319     },
14320     
14321     hide : function()
14322     {
14323         this.picker().hide();
14324         this.pop.hide();
14325         
14326         this.fireEvent('hide', this, this.date);
14327     },
14328     
14329     setTime : function()
14330     {
14331         this.hide();
14332         this.setValue(this.time.format(this.format));
14333         
14334         this.fireEvent('select', this, this.date);
14335         
14336         
14337     },
14338     
14339     onMousedown: function(e){
14340         e.stopPropagation();
14341         e.preventDefault();
14342     },
14343     
14344     onIncrementHours: function()
14345     {
14346         Roo.log('onIncrementHours');
14347         this.time = this.time.add(Date.HOUR, 1);
14348         this.update();
14349         
14350     },
14351     
14352     onDecrementHours: function()
14353     {
14354         Roo.log('onDecrementHours');
14355         this.time = this.time.add(Date.HOUR, -1);
14356         this.update();
14357     },
14358     
14359     onIncrementMinutes: function()
14360     {
14361         Roo.log('onIncrementMinutes');
14362         this.time = this.time.add(Date.MINUTE, 1);
14363         this.update();
14364     },
14365     
14366     onDecrementMinutes: function()
14367     {
14368         Roo.log('onDecrementMinutes');
14369         this.time = this.time.add(Date.MINUTE, -1);
14370         this.update();
14371     },
14372     
14373     onTogglePeriod: function()
14374     {
14375         Roo.log('onTogglePeriod');
14376         this.time = this.time.add(Date.HOUR, 12);
14377         this.update();
14378     }
14379     
14380    
14381 });
14382
14383 Roo.apply(Roo.bootstrap.TimeField,  {
14384     
14385     content : {
14386         tag: 'tbody',
14387         cn: [
14388             {
14389                 tag: 'tr',
14390                 cn: [
14391                 {
14392                     tag: 'td',
14393                     colspan: '7'
14394                 }
14395                 ]
14396             }
14397         ]
14398     },
14399     
14400     footer : {
14401         tag: 'tfoot',
14402         cn: [
14403             {
14404                 tag: 'tr',
14405                 cn: [
14406                 {
14407                     tag: 'th',
14408                     colspan: '7',
14409                     cls: '',
14410                     cn: [
14411                         {
14412                             tag: 'button',
14413                             cls: 'btn btn-info ok',
14414                             html: 'OK'
14415                         }
14416                     ]
14417                 }
14418
14419                 ]
14420             }
14421         ]
14422     }
14423 });
14424
14425 Roo.apply(Roo.bootstrap.TimeField,  {
14426   
14427     template : {
14428         tag: 'div',
14429         cls: 'datepicker dropdown-menu',
14430         cn: [
14431             {
14432                 tag: 'div',
14433                 cls: 'datepicker-time',
14434                 cn: [
14435                 {
14436                     tag: 'table',
14437                     cls: 'table-condensed',
14438                     cn:[
14439                     Roo.bootstrap.TimeField.content,
14440                     Roo.bootstrap.TimeField.footer
14441                     ]
14442                 }
14443                 ]
14444             }
14445         ]
14446     }
14447 });
14448
14449  
14450
14451  /*
14452  * - LGPL
14453  *
14454  * CheckBox
14455  * 
14456  */
14457
14458 /**
14459  * @class Roo.bootstrap.CheckBox
14460  * @extends Roo.bootstrap.Input
14461  * Bootstrap CheckBox class
14462  * 
14463  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
14464  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
14465  * @cfg {String} boxLabel The text that appears beside the checkbox
14466  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
14467  * @cfg {Boolean} checked initnal the element
14468  * 
14469  * 
14470  * @constructor
14471  * Create a new CheckBox
14472  * @param {Object} config The config object
14473  */
14474
14475 Roo.bootstrap.CheckBox = function(config){
14476     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
14477    
14478         this.addEvents({
14479             /**
14480             * @event check
14481             * Fires when the element is checked or unchecked.
14482             * @param {Roo.bootstrap.CheckBox} this This input
14483             * @param {Boolean} checked The new checked value
14484             */
14485            check : true
14486         });
14487 };
14488
14489 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
14490     
14491     inputType: 'checkbox',
14492     inputValue: 1,
14493     valueOff: 0,
14494     boxLabel: false,
14495     checked: false,
14496     weight : false,
14497     
14498     getAutoCreate : function()
14499     {
14500         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
14501         
14502         var id = Roo.id();
14503         
14504         var cfg = {};
14505         
14506         cfg.cls = 'form-group checkbox' //input-group
14507         
14508         
14509         
14510         
14511         var input =  {
14512             tag: 'input',
14513             id : id,
14514             type : this.inputType,
14515             value : (!this.checked) ? this.valueOff : this.inputValue,
14516             cls : 'roo-checkbox', //'form-box',
14517             placeholder : this.placeholder || ''
14518             
14519         };
14520         
14521         if (this.weight) { // Validity check?
14522             cfg.cls += " checkbox-" + this.weight;
14523         }
14524         
14525         if (this.disabled) {
14526             input.disabled=true;
14527         }
14528         
14529         if(this.checked){
14530             input.checked = this.checked;
14531         }
14532         
14533         if (this.name) {
14534             input.name = this.name;
14535         }
14536         
14537         if (this.size) {
14538             input.cls += ' input-' + this.size;
14539         }
14540         
14541         var settings=this;
14542         ['xs','sm','md','lg'].map(function(size){
14543             if (settings[size]) {
14544                 cfg.cls += ' col-' + size + '-' + settings[size];
14545             }
14546         });
14547         
14548        
14549         
14550         var inputblock = input;
14551         
14552         
14553         
14554         
14555         if (this.before || this.after) {
14556             
14557             inputblock = {
14558                 cls : 'input-group',
14559                 cn :  [] 
14560             };
14561             if (this.before) {
14562                 inputblock.cn.push({
14563                     tag :'span',
14564                     cls : 'input-group-addon',
14565                     html : this.before
14566                 });
14567             }
14568             inputblock.cn.push(input);
14569             if (this.after) {
14570                 inputblock.cn.push({
14571                     tag :'span',
14572                     cls : 'input-group-addon',
14573                     html : this.after
14574                 });
14575             }
14576             
14577         };
14578         
14579         if (align ==='left' && this.fieldLabel.length) {
14580                 Roo.log("left and has label");
14581                 cfg.cn = [
14582                     
14583                     {
14584                         tag: 'label',
14585                         'for' :  id,
14586                         cls : 'control-label col-md-' + this.labelWidth,
14587                         html : this.fieldLabel
14588                         
14589                     },
14590                     {
14591                         cls : "col-md-" + (12 - this.labelWidth), 
14592                         cn: [
14593                             inputblock
14594                         ]
14595                     }
14596                     
14597                 ];
14598         } else if ( this.fieldLabel.length) {
14599                 Roo.log(" label");
14600                 cfg.cn = [
14601                    
14602                     {
14603                         tag: this.boxLabel ? 'span' : 'label',
14604                         'for': id,
14605                         cls: 'control-label box-input-label',
14606                         //cls : 'input-group-addon',
14607                         html : this.fieldLabel
14608                         
14609                     },
14610                     
14611                     inputblock
14612                     
14613                 ];
14614
14615         } else {
14616             
14617                 Roo.log(" no label && no align");
14618                 cfg.cn = [  inputblock ] ;
14619                 
14620                 
14621         };
14622          if(this.boxLabel){
14623             cfg.cn.push( {
14624                 tag: 'label',
14625                 'for': id,
14626                 cls: 'box-label',
14627                 html: this.boxLabel
14628                 
14629             });
14630         }
14631         
14632         
14633        
14634         return cfg;
14635         
14636     },
14637     
14638     /**
14639      * return the real input element.
14640      */
14641     inputEl: function ()
14642     {
14643         return this.el.select('input.roo-checkbox',true).first();
14644     },
14645     
14646     label: function()
14647     {
14648         return this.el.select('label.control-label',true).first();
14649     },
14650     
14651     initEvents : function()
14652     {
14653 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
14654         
14655         this.inputEl().on('click', this.onClick,  this);
14656         
14657     },
14658     
14659     onClick : function()
14660     {   
14661         this.setChecked(!this.checked);
14662     },
14663     
14664     setChecked : function(state,suppressEvent)
14665     {
14666         this.checked = state;
14667         
14668         this.inputEl().dom.checked = state;
14669         
14670         if(suppressEvent !== true){
14671             this.fireEvent('check', this, state);
14672         }
14673         
14674         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14675         
14676     },
14677     
14678     setValue : function(v,suppressEvent)
14679     {
14680         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
14681     }
14682     
14683 });
14684
14685  
14686 /*
14687  * - LGPL
14688  *
14689  * Radio
14690  * 
14691  */
14692
14693 /**
14694  * @class Roo.bootstrap.Radio
14695  * @extends Roo.bootstrap.CheckBox
14696  * Bootstrap Radio class
14697
14698  * @constructor
14699  * Create a new Radio
14700  * @param {Object} config The config object
14701  */
14702
14703 Roo.bootstrap.Radio = function(config){
14704     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
14705    
14706 };
14707
14708 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
14709     
14710     inputType: 'radio',
14711     inputValue: '',
14712     valueOff: '',
14713     
14714     getAutoCreate : function()
14715     {
14716         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
14717         
14718         var id = Roo.id();
14719         
14720         var cfg = {};
14721         
14722         cfg.cls = 'form-group radio' //input-group
14723         
14724         var input =  {
14725             tag: 'input',
14726             id : id,
14727             type : this.inputType,
14728             value : (!this.checked) ? this.valueOff : this.inputValue,
14729             cls : 'roo-radio',
14730             placeholder : this.placeholder || ''
14731             
14732         };
14733           if (this.weight) { // Validity check?
14734             cfg.cls += " radio-" + this.weight;
14735         }
14736         if (this.disabled) {
14737             input.disabled=true;
14738         }
14739         
14740         if(this.checked){
14741             input.checked = this.checked;
14742         }
14743         
14744         if (this.name) {
14745             input.name = this.name;
14746         }
14747         
14748         if (this.size) {
14749             input.cls += ' input-' + this.size;
14750         }
14751         
14752         var settings=this;
14753         ['xs','sm','md','lg'].map(function(size){
14754             if (settings[size]) {
14755                 cfg.cls += ' col-' + size + '-' + settings[size];
14756             }
14757         });
14758         
14759         var inputblock = input;
14760         
14761         if (this.before || this.after) {
14762             
14763             inputblock = {
14764                 cls : 'input-group',
14765                 cn :  [] 
14766             };
14767             if (this.before) {
14768                 inputblock.cn.push({
14769                     tag :'span',
14770                     cls : 'input-group-addon',
14771                     html : this.before
14772                 });
14773             }
14774             inputblock.cn.push(input);
14775             if (this.after) {
14776                 inputblock.cn.push({
14777                     tag :'span',
14778                     cls : 'input-group-addon',
14779                     html : this.after
14780                 });
14781             }
14782             
14783         };
14784         
14785         if (align ==='left' && this.fieldLabel.length) {
14786                 Roo.log("left and has label");
14787                 cfg.cn = [
14788                     
14789                     {
14790                         tag: 'label',
14791                         'for' :  id,
14792                         cls : 'control-label col-md-' + this.labelWidth,
14793                         html : this.fieldLabel
14794                         
14795                     },
14796                     {
14797                         cls : "col-md-" + (12 - this.labelWidth), 
14798                         cn: [
14799                             inputblock
14800                         ]
14801                     }
14802                     
14803                 ];
14804         } else if ( this.fieldLabel.length) {
14805                 Roo.log(" label");
14806                  cfg.cn = [
14807                    
14808                     {
14809                         tag: 'label',
14810                         'for': id,
14811                         cls: 'control-label box-input-label',
14812                         //cls : 'input-group-addon',
14813                         html : this.fieldLabel
14814                         
14815                     },
14816                     
14817                     inputblock
14818                     
14819                 ];
14820
14821         } else {
14822             
14823                    Roo.log(" no label && no align");
14824                 cfg.cn = [
14825                     
14826                         inputblock
14827                     
14828                 ];
14829                 
14830                 
14831         };
14832         
14833         if(this.boxLabel){
14834             cfg.cn.push({
14835                 tag: 'label',
14836                 'for': id,
14837                 cls: 'box-label',
14838                 html: this.boxLabel
14839             })
14840         }
14841         
14842         return cfg;
14843         
14844     },
14845     inputEl: function ()
14846     {
14847         return this.el.select('input.roo-radio',true).first();
14848     },
14849     onClick : function()
14850     {   
14851         this.setChecked(true);
14852     },
14853     
14854     setChecked : function(state,suppressEvent)
14855     {
14856         if(state){
14857             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14858                 v.dom.checked = false;
14859             });
14860         }
14861         
14862         this.checked = state;
14863         this.inputEl().dom.checked = state;
14864         
14865         if(suppressEvent !== true){
14866             this.fireEvent('check', this, state);
14867         }
14868         
14869         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14870         
14871     },
14872     
14873     getGroupValue : function()
14874     {
14875         var value = ''
14876         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14877             if(v.dom.checked == true){
14878                 value = v.dom.value;
14879             }
14880         });
14881         
14882         return value;
14883     },
14884     
14885     /**
14886      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
14887      * @return {Mixed} value The field value
14888      */
14889     getValue : function(){
14890         return this.getGroupValue();
14891     }
14892     
14893 });
14894
14895  
14896 //<script type="text/javascript">
14897
14898 /*
14899  * Based  Ext JS Library 1.1.1
14900  * Copyright(c) 2006-2007, Ext JS, LLC.
14901  * LGPL
14902  *
14903  */
14904  
14905 /**
14906  * @class Roo.HtmlEditorCore
14907  * @extends Roo.Component
14908  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
14909  *
14910  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
14911  */
14912
14913 Roo.HtmlEditorCore = function(config){
14914     
14915     
14916     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
14917     this.addEvents({
14918         /**
14919          * @event initialize
14920          * Fires when the editor is fully initialized (including the iframe)
14921          * @param {Roo.HtmlEditorCore} this
14922          */
14923         initialize: true,
14924         /**
14925          * @event activate
14926          * Fires when the editor is first receives the focus. Any insertion must wait
14927          * until after this event.
14928          * @param {Roo.HtmlEditorCore} this
14929          */
14930         activate: true,
14931          /**
14932          * @event beforesync
14933          * Fires before the textarea is updated with content from the editor iframe. Return false
14934          * to cancel the sync.
14935          * @param {Roo.HtmlEditorCore} this
14936          * @param {String} html
14937          */
14938         beforesync: true,
14939          /**
14940          * @event beforepush
14941          * Fires before the iframe editor is updated with content from the textarea. Return false
14942          * to cancel the push.
14943          * @param {Roo.HtmlEditorCore} this
14944          * @param {String} html
14945          */
14946         beforepush: true,
14947          /**
14948          * @event sync
14949          * Fires when the textarea is updated with content from the editor iframe.
14950          * @param {Roo.HtmlEditorCore} this
14951          * @param {String} html
14952          */
14953         sync: true,
14954          /**
14955          * @event push
14956          * Fires when the iframe editor is updated with content from the textarea.
14957          * @param {Roo.HtmlEditorCore} this
14958          * @param {String} html
14959          */
14960         push: true,
14961         
14962         /**
14963          * @event editorevent
14964          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14965          * @param {Roo.HtmlEditorCore} this
14966          */
14967         editorevent: true
14968     });
14969      
14970 };
14971
14972
14973 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
14974
14975
14976      /**
14977      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
14978      */
14979     
14980     owner : false,
14981     
14982      /**
14983      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14984      *                        Roo.resizable.
14985      */
14986     resizable : false,
14987      /**
14988      * @cfg {Number} height (in pixels)
14989      */   
14990     height: 300,
14991    /**
14992      * @cfg {Number} width (in pixels)
14993      */   
14994     width: 500,
14995     
14996     /**
14997      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14998      * 
14999      */
15000     stylesheets: false,
15001     
15002     // id of frame..
15003     frameId: false,
15004     
15005     // private properties
15006     validationEvent : false,
15007     deferHeight: true,
15008     initialized : false,
15009     activated : false,
15010     sourceEditMode : false,
15011     onFocus : Roo.emptyFn,
15012     iframePad:3,
15013     hideMode:'offsets',
15014     
15015     clearUp: true,
15016     
15017      
15018     
15019
15020     /**
15021      * Protected method that will not generally be called directly. It
15022      * is called when the editor initializes the iframe with HTML contents. Override this method if you
15023      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
15024      */
15025     getDocMarkup : function(){
15026         // body styles..
15027         var st = '';
15028         Roo.log(this.stylesheets);
15029         
15030         // inherit styels from page...?? 
15031         if (this.stylesheets === false) {
15032             
15033             Roo.get(document.head).select('style').each(function(node) {
15034                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
15035             });
15036             
15037             Roo.get(document.head).select('link').each(function(node) { 
15038                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
15039             });
15040             
15041         } else if (!this.stylesheets.length) {
15042                 // simple..
15043                 st = '<style type="text/css">' +
15044                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
15045                    '</style>';
15046         } else {
15047             Roo.each(this.stylesheets, function(s) {
15048                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
15049             });
15050             
15051         }
15052         
15053         st +=  '<style type="text/css">' +
15054             'IMG { cursor: pointer } ' +
15055         '</style>';
15056
15057         
15058         return '<html><head>' + st  +
15059             //<style type="text/css">' +
15060             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
15061             //'</style>' +
15062             ' </head><body class="roo-htmleditor-body"></body></html>';
15063     },
15064
15065     // private
15066     onRender : function(ct, position)
15067     {
15068         var _t = this;
15069         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
15070         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
15071         
15072         
15073         this.el.dom.style.border = '0 none';
15074         this.el.dom.setAttribute('tabIndex', -1);
15075         this.el.addClass('x-hidden hide');
15076         
15077         
15078         
15079         if(Roo.isIE){ // fix IE 1px bogus margin
15080             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
15081         }
15082        
15083         
15084         this.frameId = Roo.id();
15085         
15086          
15087         
15088         var iframe = this.owner.wrap.createChild({
15089             tag: 'iframe',
15090             cls: 'form-control', // bootstrap..
15091             id: this.frameId,
15092             name: this.frameId,
15093             frameBorder : 'no',
15094             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
15095         }, this.el
15096         );
15097         
15098         
15099         this.iframe = iframe.dom;
15100
15101          this.assignDocWin();
15102         
15103         this.doc.designMode = 'on';
15104        
15105         this.doc.open();
15106         this.doc.write(this.getDocMarkup());
15107         this.doc.close();
15108
15109         
15110         var task = { // must defer to wait for browser to be ready
15111             run : function(){
15112                 //console.log("run task?" + this.doc.readyState);
15113                 this.assignDocWin();
15114                 if(this.doc.body || this.doc.readyState == 'complete'){
15115                     try {
15116                         this.doc.designMode="on";
15117                     } catch (e) {
15118                         return;
15119                     }
15120                     Roo.TaskMgr.stop(task);
15121                     this.initEditor.defer(10, this);
15122                 }
15123             },
15124             interval : 10,
15125             duration: 10000,
15126             scope: this
15127         };
15128         Roo.TaskMgr.start(task);
15129
15130         
15131          
15132     },
15133
15134     // private
15135     onResize : function(w, h)
15136     {
15137          Roo.log('resize: ' +w + ',' + h );
15138         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
15139         if(!this.iframe){
15140             return;
15141         }
15142         if(typeof w == 'number'){
15143             
15144             this.iframe.style.width = w + 'px';
15145         }
15146         if(typeof h == 'number'){
15147             
15148             this.iframe.style.height = h + 'px';
15149             if(this.doc){
15150                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
15151             }
15152         }
15153         
15154     },
15155
15156     /**
15157      * Toggles the editor between standard and source edit mode.
15158      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15159      */
15160     toggleSourceEdit : function(sourceEditMode){
15161         
15162         this.sourceEditMode = sourceEditMode === true;
15163         
15164         if(this.sourceEditMode){
15165  
15166             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
15167             
15168         }else{
15169             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
15170             //this.iframe.className = '';
15171             this.deferFocus();
15172         }
15173         //this.setSize(this.owner.wrap.getSize());
15174         //this.fireEvent('editmodechange', this, this.sourceEditMode);
15175     },
15176
15177     
15178   
15179
15180     /**
15181      * Protected method that will not generally be called directly. If you need/want
15182      * custom HTML cleanup, this is the method you should override.
15183      * @param {String} html The HTML to be cleaned
15184      * return {String} The cleaned HTML
15185      */
15186     cleanHtml : function(html){
15187         html = String(html);
15188         if(html.length > 5){
15189             if(Roo.isSafari){ // strip safari nonsense
15190                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
15191             }
15192         }
15193         if(html == '&nbsp;'){
15194             html = '';
15195         }
15196         return html;
15197     },
15198
15199     /**
15200      * HTML Editor -> Textarea
15201      * Protected method that will not generally be called directly. Syncs the contents
15202      * of the editor iframe with the textarea.
15203      */
15204     syncValue : function(){
15205         if(this.initialized){
15206             var bd = (this.doc.body || this.doc.documentElement);
15207             //this.cleanUpPaste(); -- this is done else where and causes havoc..
15208             var html = bd.innerHTML;
15209             if(Roo.isSafari){
15210                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
15211                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
15212                 if(m && m[1]){
15213                     html = '<div style="'+m[0]+'">' + html + '</div>';
15214                 }
15215             }
15216             html = this.cleanHtml(html);
15217             // fix up the special chars.. normaly like back quotes in word...
15218             // however we do not want to do this with chinese..
15219             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
15220                 var cc = b.charCodeAt();
15221                 if (
15222                     (cc >= 0x4E00 && cc < 0xA000 ) ||
15223                     (cc >= 0x3400 && cc < 0x4E00 ) ||
15224                     (cc >= 0xf900 && cc < 0xfb00 )
15225                 ) {
15226                         return b;
15227                 }
15228                 return "&#"+cc+";" 
15229             });
15230             if(this.owner.fireEvent('beforesync', this, html) !== false){
15231                 this.el.dom.value = html;
15232                 this.owner.fireEvent('sync', this, html);
15233             }
15234         }
15235     },
15236
15237     /**
15238      * Protected method that will not generally be called directly. Pushes the value of the textarea
15239      * into the iframe editor.
15240      */
15241     pushValue : function(){
15242         if(this.initialized){
15243             var v = this.el.dom.value.trim();
15244             
15245 //            if(v.length < 1){
15246 //                v = '&#160;';
15247 //            }
15248             
15249             if(this.owner.fireEvent('beforepush', this, v) !== false){
15250                 var d = (this.doc.body || this.doc.documentElement);
15251                 d.innerHTML = v;
15252                 this.cleanUpPaste();
15253                 this.el.dom.value = d.innerHTML;
15254                 this.owner.fireEvent('push', this, v);
15255             }
15256         }
15257     },
15258
15259     // private
15260     deferFocus : function(){
15261         this.focus.defer(10, this);
15262     },
15263
15264     // doc'ed in Field
15265     focus : function(){
15266         if(this.win && !this.sourceEditMode){
15267             this.win.focus();
15268         }else{
15269             this.el.focus();
15270         }
15271     },
15272     
15273     assignDocWin: function()
15274     {
15275         var iframe = this.iframe;
15276         
15277          if(Roo.isIE){
15278             this.doc = iframe.contentWindow.document;
15279             this.win = iframe.contentWindow;
15280         } else {
15281             if (!Roo.get(this.frameId)) {
15282                 return;
15283             }
15284             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
15285             this.win = Roo.get(this.frameId).dom.contentWindow;
15286         }
15287     },
15288     
15289     // private
15290     initEditor : function(){
15291         //console.log("INIT EDITOR");
15292         this.assignDocWin();
15293         
15294         
15295         
15296         this.doc.designMode="on";
15297         this.doc.open();
15298         this.doc.write(this.getDocMarkup());
15299         this.doc.close();
15300         
15301         var dbody = (this.doc.body || this.doc.documentElement);
15302         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
15303         // this copies styles from the containing element into thsi one..
15304         // not sure why we need all of this..
15305         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
15306         
15307         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
15308         //ss['background-attachment'] = 'fixed'; // w3c
15309         dbody.bgProperties = 'fixed'; // ie
15310         //Roo.DomHelper.applyStyles(dbody, ss);
15311         Roo.EventManager.on(this.doc, {
15312             //'mousedown': this.onEditorEvent,
15313             'mouseup': this.onEditorEvent,
15314             'dblclick': this.onEditorEvent,
15315             'click': this.onEditorEvent,
15316             'keyup': this.onEditorEvent,
15317             buffer:100,
15318             scope: this
15319         });
15320         if(Roo.isGecko){
15321             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
15322         }
15323         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
15324             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
15325         }
15326         this.initialized = true;
15327
15328         this.owner.fireEvent('initialize', this);
15329         this.pushValue();
15330     },
15331
15332     // private
15333     onDestroy : function(){
15334         
15335         
15336         
15337         if(this.rendered){
15338             
15339             //for (var i =0; i < this.toolbars.length;i++) {
15340             //    // fixme - ask toolbars for heights?
15341             //    this.toolbars[i].onDestroy();
15342            // }
15343             
15344             //this.wrap.dom.innerHTML = '';
15345             //this.wrap.remove();
15346         }
15347     },
15348
15349     // private
15350     onFirstFocus : function(){
15351         
15352         this.assignDocWin();
15353         
15354         
15355         this.activated = true;
15356          
15357     
15358         if(Roo.isGecko){ // prevent silly gecko errors
15359             this.win.focus();
15360             var s = this.win.getSelection();
15361             if(!s.focusNode || s.focusNode.nodeType != 3){
15362                 var r = s.getRangeAt(0);
15363                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
15364                 r.collapse(true);
15365                 this.deferFocus();
15366             }
15367             try{
15368                 this.execCmd('useCSS', true);
15369                 this.execCmd('styleWithCSS', false);
15370             }catch(e){}
15371         }
15372         this.owner.fireEvent('activate', this);
15373     },
15374
15375     // private
15376     adjustFont: function(btn){
15377         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
15378         //if(Roo.isSafari){ // safari
15379         //    adjust *= 2;
15380        // }
15381         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
15382         if(Roo.isSafari){ // safari
15383             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
15384             v =  (v < 10) ? 10 : v;
15385             v =  (v > 48) ? 48 : v;
15386             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
15387             
15388         }
15389         
15390         
15391         v = Math.max(1, v+adjust);
15392         
15393         this.execCmd('FontSize', v  );
15394     },
15395
15396     onEditorEvent : function(e){
15397         this.owner.fireEvent('editorevent', this, e);
15398       //  this.updateToolbar();
15399         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
15400     },
15401
15402     insertTag : function(tg)
15403     {
15404         // could be a bit smarter... -> wrap the current selected tRoo..
15405         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
15406             
15407             range = this.createRange(this.getSelection());
15408             var wrappingNode = this.doc.createElement(tg.toLowerCase());
15409             wrappingNode.appendChild(range.extractContents());
15410             range.insertNode(wrappingNode);
15411
15412             return;
15413             
15414             
15415             
15416         }
15417         this.execCmd("formatblock",   tg);
15418         
15419     },
15420     
15421     insertText : function(txt)
15422     {
15423         
15424         
15425         var range = this.createRange();
15426         range.deleteContents();
15427                //alert(Sender.getAttribute('label'));
15428                
15429         range.insertNode(this.doc.createTextNode(txt));
15430     } ,
15431     
15432      
15433
15434     /**
15435      * Executes a Midas editor command on the editor document and performs necessary focus and
15436      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
15437      * @param {String} cmd The Midas command
15438      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
15439      */
15440     relayCmd : function(cmd, value){
15441         this.win.focus();
15442         this.execCmd(cmd, value);
15443         this.owner.fireEvent('editorevent', this);
15444         //this.updateToolbar();
15445         this.owner.deferFocus();
15446     },
15447
15448     /**
15449      * Executes a Midas editor command directly on the editor document.
15450      * For visual commands, you should use {@link #relayCmd} instead.
15451      * <b>This should only be called after the editor is initialized.</b>
15452      * @param {String} cmd The Midas command
15453      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
15454      */
15455     execCmd : function(cmd, value){
15456         this.doc.execCommand(cmd, false, value === undefined ? null : value);
15457         this.syncValue();
15458     },
15459  
15460  
15461    
15462     /**
15463      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
15464      * to insert tRoo.
15465      * @param {String} text | dom node.. 
15466      */
15467     insertAtCursor : function(text)
15468     {
15469         
15470         
15471         
15472         if(!this.activated){
15473             return;
15474         }
15475         /*
15476         if(Roo.isIE){
15477             this.win.focus();
15478             var r = this.doc.selection.createRange();
15479             if(r){
15480                 r.collapse(true);
15481                 r.pasteHTML(text);
15482                 this.syncValue();
15483                 this.deferFocus();
15484             
15485             }
15486             return;
15487         }
15488         */
15489         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
15490             this.win.focus();
15491             
15492             
15493             // from jquery ui (MIT licenced)
15494             var range, node;
15495             var win = this.win;
15496             
15497             if (win.getSelection && win.getSelection().getRangeAt) {
15498                 range = win.getSelection().getRangeAt(0);
15499                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
15500                 range.insertNode(node);
15501             } else if (win.document.selection && win.document.selection.createRange) {
15502                 // no firefox support
15503                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
15504                 win.document.selection.createRange().pasteHTML(txt);
15505             } else {
15506                 // no firefox support
15507                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
15508                 this.execCmd('InsertHTML', txt);
15509             } 
15510             
15511             this.syncValue();
15512             
15513             this.deferFocus();
15514         }
15515     },
15516  // private
15517     mozKeyPress : function(e){
15518         if(e.ctrlKey){
15519             var c = e.getCharCode(), cmd;
15520           
15521             if(c > 0){
15522                 c = String.fromCharCode(c).toLowerCase();
15523                 switch(c){
15524                     case 'b':
15525                         cmd = 'bold';
15526                         break;
15527                     case 'i':
15528                         cmd = 'italic';
15529                         break;
15530                     
15531                     case 'u':
15532                         cmd = 'underline';
15533                         break;
15534                     
15535                     case 'v':
15536                         this.cleanUpPaste.defer(100, this);
15537                         return;
15538                         
15539                 }
15540                 if(cmd){
15541                     this.win.focus();
15542                     this.execCmd(cmd);
15543                     this.deferFocus();
15544                     e.preventDefault();
15545                 }
15546                 
15547             }
15548         }
15549     },
15550
15551     // private
15552     fixKeys : function(){ // load time branching for fastest keydown performance
15553         if(Roo.isIE){
15554             return function(e){
15555                 var k = e.getKey(), r;
15556                 if(k == e.TAB){
15557                     e.stopEvent();
15558                     r = this.doc.selection.createRange();
15559                     if(r){
15560                         r.collapse(true);
15561                         r.pasteHTML('&#160;&#160;&#160;&#160;');
15562                         this.deferFocus();
15563                     }
15564                     return;
15565                 }
15566                 
15567                 if(k == e.ENTER){
15568                     r = this.doc.selection.createRange();
15569                     if(r){
15570                         var target = r.parentElement();
15571                         if(!target || target.tagName.toLowerCase() != 'li'){
15572                             e.stopEvent();
15573                             r.pasteHTML('<br />');
15574                             r.collapse(false);
15575                             r.select();
15576                         }
15577                     }
15578                 }
15579                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15580                     this.cleanUpPaste.defer(100, this);
15581                     return;
15582                 }
15583                 
15584                 
15585             };
15586         }else if(Roo.isOpera){
15587             return function(e){
15588                 var k = e.getKey();
15589                 if(k == e.TAB){
15590                     e.stopEvent();
15591                     this.win.focus();
15592                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
15593                     this.deferFocus();
15594                 }
15595                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15596                     this.cleanUpPaste.defer(100, this);
15597                     return;
15598                 }
15599                 
15600             };
15601         }else if(Roo.isSafari){
15602             return function(e){
15603                 var k = e.getKey();
15604                 
15605                 if(k == e.TAB){
15606                     e.stopEvent();
15607                     this.execCmd('InsertText','\t');
15608                     this.deferFocus();
15609                     return;
15610                 }
15611                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15612                     this.cleanUpPaste.defer(100, this);
15613                     return;
15614                 }
15615                 
15616              };
15617         }
15618     }(),
15619     
15620     getAllAncestors: function()
15621     {
15622         var p = this.getSelectedNode();
15623         var a = [];
15624         if (!p) {
15625             a.push(p); // push blank onto stack..
15626             p = this.getParentElement();
15627         }
15628         
15629         
15630         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
15631             a.push(p);
15632             p = p.parentNode;
15633         }
15634         a.push(this.doc.body);
15635         return a;
15636     },
15637     lastSel : false,
15638     lastSelNode : false,
15639     
15640     
15641     getSelection : function() 
15642     {
15643         this.assignDocWin();
15644         return Roo.isIE ? this.doc.selection : this.win.getSelection();
15645     },
15646     
15647     getSelectedNode: function() 
15648     {
15649         // this may only work on Gecko!!!
15650         
15651         // should we cache this!!!!
15652         
15653         
15654         
15655          
15656         var range = this.createRange(this.getSelection()).cloneRange();
15657         
15658         if (Roo.isIE) {
15659             var parent = range.parentElement();
15660             while (true) {
15661                 var testRange = range.duplicate();
15662                 testRange.moveToElementText(parent);
15663                 if (testRange.inRange(range)) {
15664                     break;
15665                 }
15666                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
15667                     break;
15668                 }
15669                 parent = parent.parentElement;
15670             }
15671             return parent;
15672         }
15673         
15674         // is ancestor a text element.
15675         var ac =  range.commonAncestorContainer;
15676         if (ac.nodeType == 3) {
15677             ac = ac.parentNode;
15678         }
15679         
15680         var ar = ac.childNodes;
15681          
15682         var nodes = [];
15683         var other_nodes = [];
15684         var has_other_nodes = false;
15685         for (var i=0;i<ar.length;i++) {
15686             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
15687                 continue;
15688             }
15689             // fullly contained node.
15690             
15691             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
15692                 nodes.push(ar[i]);
15693                 continue;
15694             }
15695             
15696             // probably selected..
15697             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
15698                 other_nodes.push(ar[i]);
15699                 continue;
15700             }
15701             // outer..
15702             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
15703                 continue;
15704             }
15705             
15706             
15707             has_other_nodes = true;
15708         }
15709         if (!nodes.length && other_nodes.length) {
15710             nodes= other_nodes;
15711         }
15712         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
15713             return false;
15714         }
15715         
15716         return nodes[0];
15717     },
15718     createRange: function(sel)
15719     {
15720         // this has strange effects when using with 
15721         // top toolbar - not sure if it's a great idea.
15722         //this.editor.contentWindow.focus();
15723         if (typeof sel != "undefined") {
15724             try {
15725                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
15726             } catch(e) {
15727                 return this.doc.createRange();
15728             }
15729         } else {
15730             return this.doc.createRange();
15731         }
15732     },
15733     getParentElement: function()
15734     {
15735         
15736         this.assignDocWin();
15737         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
15738         
15739         var range = this.createRange(sel);
15740          
15741         try {
15742             var p = range.commonAncestorContainer;
15743             while (p.nodeType == 3) { // text node
15744                 p = p.parentNode;
15745             }
15746             return p;
15747         } catch (e) {
15748             return null;
15749         }
15750     
15751     },
15752     /***
15753      *
15754      * Range intersection.. the hard stuff...
15755      *  '-1' = before
15756      *  '0' = hits..
15757      *  '1' = after.
15758      *         [ -- selected range --- ]
15759      *   [fail]                        [fail]
15760      *
15761      *    basically..
15762      *      if end is before start or  hits it. fail.
15763      *      if start is after end or hits it fail.
15764      *
15765      *   if either hits (but other is outside. - then it's not 
15766      *   
15767      *    
15768      **/
15769     
15770     
15771     // @see http://www.thismuchiknow.co.uk/?p=64.
15772     rangeIntersectsNode : function(range, node)
15773     {
15774         var nodeRange = node.ownerDocument.createRange();
15775         try {
15776             nodeRange.selectNode(node);
15777         } catch (e) {
15778             nodeRange.selectNodeContents(node);
15779         }
15780     
15781         var rangeStartRange = range.cloneRange();
15782         rangeStartRange.collapse(true);
15783     
15784         var rangeEndRange = range.cloneRange();
15785         rangeEndRange.collapse(false);
15786     
15787         var nodeStartRange = nodeRange.cloneRange();
15788         nodeStartRange.collapse(true);
15789     
15790         var nodeEndRange = nodeRange.cloneRange();
15791         nodeEndRange.collapse(false);
15792     
15793         return rangeStartRange.compareBoundaryPoints(
15794                  Range.START_TO_START, nodeEndRange) == -1 &&
15795                rangeEndRange.compareBoundaryPoints(
15796                  Range.START_TO_START, nodeStartRange) == 1;
15797         
15798          
15799     },
15800     rangeCompareNode : function(range, node)
15801     {
15802         var nodeRange = node.ownerDocument.createRange();
15803         try {
15804             nodeRange.selectNode(node);
15805         } catch (e) {
15806             nodeRange.selectNodeContents(node);
15807         }
15808         
15809         
15810         range.collapse(true);
15811     
15812         nodeRange.collapse(true);
15813      
15814         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
15815         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
15816          
15817         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
15818         
15819         var nodeIsBefore   =  ss == 1;
15820         var nodeIsAfter    = ee == -1;
15821         
15822         if (nodeIsBefore && nodeIsAfter)
15823             return 0; // outer
15824         if (!nodeIsBefore && nodeIsAfter)
15825             return 1; //right trailed.
15826         
15827         if (nodeIsBefore && !nodeIsAfter)
15828             return 2;  // left trailed.
15829         // fully contined.
15830         return 3;
15831     },
15832
15833     // private? - in a new class?
15834     cleanUpPaste :  function()
15835     {
15836         // cleans up the whole document..
15837         Roo.log('cleanuppaste');
15838         
15839         this.cleanUpChildren(this.doc.body);
15840         var clean = this.cleanWordChars(this.doc.body.innerHTML);
15841         if (clean != this.doc.body.innerHTML) {
15842             this.doc.body.innerHTML = clean;
15843         }
15844         
15845     },
15846     
15847     cleanWordChars : function(input) {// change the chars to hex code
15848         var he = Roo.HtmlEditorCore;
15849         
15850         var output = input;
15851         Roo.each(he.swapCodes, function(sw) { 
15852             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
15853             
15854             output = output.replace(swapper, sw[1]);
15855         });
15856         
15857         return output;
15858     },
15859     
15860     
15861     cleanUpChildren : function (n)
15862     {
15863         if (!n.childNodes.length) {
15864             return;
15865         }
15866         for (var i = n.childNodes.length-1; i > -1 ; i--) {
15867            this.cleanUpChild(n.childNodes[i]);
15868         }
15869     },
15870     
15871     
15872         
15873     
15874     cleanUpChild : function (node)
15875     {
15876         var ed = this;
15877         //console.log(node);
15878         if (node.nodeName == "#text") {
15879             // clean up silly Windows -- stuff?
15880             return; 
15881         }
15882         if (node.nodeName == "#comment") {
15883             node.parentNode.removeChild(node);
15884             // clean up silly Windows -- stuff?
15885             return; 
15886         }
15887         
15888         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
15889             // remove node.
15890             node.parentNode.removeChild(node);
15891             return;
15892             
15893         }
15894         
15895         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
15896         
15897         // remove <a name=....> as rendering on yahoo mailer is borked with this.
15898         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
15899         
15900         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
15901         //    remove_keep_children = true;
15902         //}
15903         
15904         if (remove_keep_children) {
15905             this.cleanUpChildren(node);
15906             // inserts everything just before this node...
15907             while (node.childNodes.length) {
15908                 var cn = node.childNodes[0];
15909                 node.removeChild(cn);
15910                 node.parentNode.insertBefore(cn, node);
15911             }
15912             node.parentNode.removeChild(node);
15913             return;
15914         }
15915         
15916         if (!node.attributes || !node.attributes.length) {
15917             this.cleanUpChildren(node);
15918             return;
15919         }
15920         
15921         function cleanAttr(n,v)
15922         {
15923             
15924             if (v.match(/^\./) || v.match(/^\//)) {
15925                 return;
15926             }
15927             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
15928                 return;
15929             }
15930             if (v.match(/^#/)) {
15931                 return;
15932             }
15933 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
15934             node.removeAttribute(n);
15935             
15936         }
15937         
15938         function cleanStyle(n,v)
15939         {
15940             if (v.match(/expression/)) { //XSS?? should we even bother..
15941                 node.removeAttribute(n);
15942                 return;
15943             }
15944             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
15945             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
15946             
15947             
15948             var parts = v.split(/;/);
15949             var clean = [];
15950             
15951             Roo.each(parts, function(p) {
15952                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
15953                 if (!p.length) {
15954                     return true;
15955                 }
15956                 var l = p.split(':').shift().replace(/\s+/g,'');
15957                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
15958                 
15959                 if ( cblack.indexOf(l) > -1) {
15960 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15961                     //node.removeAttribute(n);
15962                     return true;
15963                 }
15964                 //Roo.log()
15965                 // only allow 'c whitelisted system attributes'
15966                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
15967 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15968                     //node.removeAttribute(n);
15969                     return true;
15970                 }
15971                 
15972                 
15973                  
15974                 
15975                 clean.push(p);
15976                 return true;
15977             });
15978             if (clean.length) { 
15979                 node.setAttribute(n, clean.join(';'));
15980             } else {
15981                 node.removeAttribute(n);
15982             }
15983             
15984         }
15985         
15986         
15987         for (var i = node.attributes.length-1; i > -1 ; i--) {
15988             var a = node.attributes[i];
15989             //console.log(a);
15990             
15991             if (a.name.toLowerCase().substr(0,2)=='on')  {
15992                 node.removeAttribute(a.name);
15993                 continue;
15994             }
15995             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
15996                 node.removeAttribute(a.name);
15997                 continue;
15998             }
15999             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
16000                 cleanAttr(a.name,a.value); // fixme..
16001                 continue;
16002             }
16003             if (a.name == 'style') {
16004                 cleanStyle(a.name,a.value);
16005                 continue;
16006             }
16007             /// clean up MS crap..
16008             // tecnically this should be a list of valid class'es..
16009             
16010             
16011             if (a.name == 'class') {
16012                 if (a.value.match(/^Mso/)) {
16013                     node.className = '';
16014                 }
16015                 
16016                 if (a.value.match(/body/)) {
16017                     node.className = '';
16018                 }
16019                 continue;
16020             }
16021             
16022             // style cleanup!?
16023             // class cleanup?
16024             
16025         }
16026         
16027         
16028         this.cleanUpChildren(node);
16029         
16030         
16031     },
16032     /**
16033      * Clean up MS wordisms...
16034      */
16035     cleanWord : function(node)
16036     {
16037         var _t = this;
16038         var cleanWordChildren = function()
16039         {
16040             if (!node.childNodes.length) {
16041                 return;
16042             }
16043             for (var i = node.childNodes.length-1; i > -1 ; i--) {
16044                _t.cleanWord(node.childNodes[i]);
16045             }
16046         }
16047         
16048         
16049         if (!node) {
16050             this.cleanWord(this.doc.body);
16051             return;
16052         }
16053         if (node.nodeName == "#text") {
16054             // clean up silly Windows -- stuff?
16055             return; 
16056         }
16057         if (node.nodeName == "#comment") {
16058             node.parentNode.removeChild(node);
16059             // clean up silly Windows -- stuff?
16060             return; 
16061         }
16062         
16063         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
16064             node.parentNode.removeChild(node);
16065             return;
16066         }
16067         
16068         // remove - but keep children..
16069         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
16070             while (node.childNodes.length) {
16071                 var cn = node.childNodes[0];
16072                 node.removeChild(cn);
16073                 node.parentNode.insertBefore(cn, node);
16074             }
16075             node.parentNode.removeChild(node);
16076             cleanWordChildren();
16077             return;
16078         }
16079         // clean styles
16080         if (node.className.length) {
16081             
16082             var cn = node.className.split(/\W+/);
16083             var cna = [];
16084             Roo.each(cn, function(cls) {
16085                 if (cls.match(/Mso[a-zA-Z]+/)) {
16086                     return;
16087                 }
16088                 cna.push(cls);
16089             });
16090             node.className = cna.length ? cna.join(' ') : '';
16091             if (!cna.length) {
16092                 node.removeAttribute("class");
16093             }
16094         }
16095         
16096         if (node.hasAttribute("lang")) {
16097             node.removeAttribute("lang");
16098         }
16099         
16100         if (node.hasAttribute("style")) {
16101             
16102             var styles = node.getAttribute("style").split(";");
16103             var nstyle = [];
16104             Roo.each(styles, function(s) {
16105                 if (!s.match(/:/)) {
16106                     return;
16107                 }
16108                 var kv = s.split(":");
16109                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
16110                     return;
16111                 }
16112                 // what ever is left... we allow.
16113                 nstyle.push(s);
16114             });
16115             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
16116             if (!nstyle.length) {
16117                 node.removeAttribute('style');
16118             }
16119         }
16120         
16121         cleanWordChildren();
16122         
16123         
16124     },
16125     domToHTML : function(currentElement, depth, nopadtext) {
16126         
16127             depth = depth || 0;
16128             nopadtext = nopadtext || false;
16129         
16130             if (!currentElement) {
16131                 return this.domToHTML(this.doc.body);
16132             }
16133             
16134             //Roo.log(currentElement);
16135             var j;
16136             var allText = false;
16137             var nodeName = currentElement.nodeName;
16138             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
16139             
16140             if  (nodeName == '#text') {
16141                 return currentElement.nodeValue;
16142             }
16143             
16144             
16145             var ret = '';
16146             if (nodeName != 'BODY') {
16147                  
16148                 var i = 0;
16149                 // Prints the node tagName, such as <A>, <IMG>, etc
16150                 if (tagName) {
16151                     var attr = [];
16152                     for(i = 0; i < currentElement.attributes.length;i++) {
16153                         // quoting?
16154                         var aname = currentElement.attributes.item(i).name;
16155                         if (!currentElement.attributes.item(i).value.length) {
16156                             continue;
16157                         }
16158                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
16159                     }
16160                     
16161                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
16162                 } 
16163                 else {
16164                     
16165                     // eack
16166                 }
16167             } else {
16168                 tagName = false;
16169             }
16170             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
16171                 return ret;
16172             }
16173             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
16174                 nopadtext = true;
16175             }
16176             
16177             
16178             // Traverse the tree
16179             i = 0;
16180             var currentElementChild = currentElement.childNodes.item(i);
16181             var allText = true;
16182             var innerHTML  = '';
16183             lastnode = '';
16184             while (currentElementChild) {
16185                 // Formatting code (indent the tree so it looks nice on the screen)
16186                 var nopad = nopadtext;
16187                 if (lastnode == 'SPAN') {
16188                     nopad  = true;
16189                 }
16190                 // text
16191                 if  (currentElementChild.nodeName == '#text') {
16192                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
16193                     if (!nopad && toadd.length > 80) {
16194                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
16195                     }
16196                     innerHTML  += toadd;
16197                     
16198                     i++;
16199                     currentElementChild = currentElement.childNodes.item(i);
16200                     lastNode = '';
16201                     continue;
16202                 }
16203                 allText = false;
16204                 
16205                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
16206                     
16207                 // Recursively traverse the tree structure of the child node
16208                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
16209                 lastnode = currentElementChild.nodeName;
16210                 i++;
16211                 currentElementChild=currentElement.childNodes.item(i);
16212             }
16213             
16214             ret += innerHTML;
16215             
16216             if (!allText) {
16217                     // The remaining code is mostly for formatting the tree
16218                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
16219             }
16220             
16221             
16222             if (tagName) {
16223                 ret+= "</"+tagName+">";
16224             }
16225             return ret;
16226             
16227         }
16228     
16229     // hide stuff that is not compatible
16230     /**
16231      * @event blur
16232      * @hide
16233      */
16234     /**
16235      * @event change
16236      * @hide
16237      */
16238     /**
16239      * @event focus
16240      * @hide
16241      */
16242     /**
16243      * @event specialkey
16244      * @hide
16245      */
16246     /**
16247      * @cfg {String} fieldClass @hide
16248      */
16249     /**
16250      * @cfg {String} focusClass @hide
16251      */
16252     /**
16253      * @cfg {String} autoCreate @hide
16254      */
16255     /**
16256      * @cfg {String} inputType @hide
16257      */
16258     /**
16259      * @cfg {String} invalidClass @hide
16260      */
16261     /**
16262      * @cfg {String} invalidText @hide
16263      */
16264     /**
16265      * @cfg {String} msgFx @hide
16266      */
16267     /**
16268      * @cfg {String} validateOnBlur @hide
16269      */
16270 });
16271
16272 Roo.HtmlEditorCore.white = [
16273         'area', 'br', 'img', 'input', 'hr', 'wbr',
16274         
16275        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
16276        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
16277        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
16278        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
16279        'table',   'ul',         'xmp', 
16280        
16281        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
16282       'thead',   'tr', 
16283      
16284       'dir', 'menu', 'ol', 'ul', 'dl',
16285        
16286       'embed',  'object'
16287 ];
16288
16289
16290 Roo.HtmlEditorCore.black = [
16291     //    'embed',  'object', // enable - backend responsiblity to clean thiese
16292         'applet', // 
16293         'base',   'basefont', 'bgsound', 'blink',  'body', 
16294         'frame',  'frameset', 'head',    'html',   'ilayer', 
16295         'iframe', 'layer',  'link',     'meta',    'object',   
16296         'script', 'style' ,'title',  'xml' // clean later..
16297 ];
16298 Roo.HtmlEditorCore.clean = [
16299     'script', 'style', 'title', 'xml'
16300 ];
16301 Roo.HtmlEditorCore.remove = [
16302     'font'
16303 ];
16304 // attributes..
16305
16306 Roo.HtmlEditorCore.ablack = [
16307     'on'
16308 ];
16309     
16310 Roo.HtmlEditorCore.aclean = [ 
16311     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
16312 ];
16313
16314 // protocols..
16315 Roo.HtmlEditorCore.pwhite= [
16316         'http',  'https',  'mailto'
16317 ];
16318
16319 // white listed style attributes.
16320 Roo.HtmlEditorCore.cwhite= [
16321       //  'text-align', /// default is to allow most things..
16322       
16323          
16324 //        'font-size'//??
16325 ];
16326
16327 // black listed style attributes.
16328 Roo.HtmlEditorCore.cblack= [
16329       //  'font-size' -- this can be set by the project 
16330 ];
16331
16332
16333 Roo.HtmlEditorCore.swapCodes   =[ 
16334     [    8211, "--" ], 
16335     [    8212, "--" ], 
16336     [    8216,  "'" ],  
16337     [    8217, "'" ],  
16338     [    8220, '"' ],  
16339     [    8221, '"' ],  
16340     [    8226, "*" ],  
16341     [    8230, "..." ]
16342 ]; 
16343
16344     /*
16345  * - LGPL
16346  *
16347  * HtmlEditor
16348  * 
16349  */
16350
16351 /**
16352  * @class Roo.bootstrap.HtmlEditor
16353  * @extends Roo.bootstrap.TextArea
16354  * Bootstrap HtmlEditor class
16355
16356  * @constructor
16357  * Create a new HtmlEditor
16358  * @param {Object} config The config object
16359  */
16360
16361 Roo.bootstrap.HtmlEditor = function(config){
16362     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
16363     if (!this.toolbars) {
16364         this.toolbars = [];
16365     }
16366     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
16367     this.addEvents({
16368             /**
16369              * @event initialize
16370              * Fires when the editor is fully initialized (including the iframe)
16371              * @param {HtmlEditor} this
16372              */
16373             initialize: true,
16374             /**
16375              * @event activate
16376              * Fires when the editor is first receives the focus. Any insertion must wait
16377              * until after this event.
16378              * @param {HtmlEditor} this
16379              */
16380             activate: true,
16381              /**
16382              * @event beforesync
16383              * Fires before the textarea is updated with content from the editor iframe. Return false
16384              * to cancel the sync.
16385              * @param {HtmlEditor} this
16386              * @param {String} html
16387              */
16388             beforesync: true,
16389              /**
16390              * @event beforepush
16391              * Fires before the iframe editor is updated with content from the textarea. Return false
16392              * to cancel the push.
16393              * @param {HtmlEditor} this
16394              * @param {String} html
16395              */
16396             beforepush: true,
16397              /**
16398              * @event sync
16399              * Fires when the textarea is updated with content from the editor iframe.
16400              * @param {HtmlEditor} this
16401              * @param {String} html
16402              */
16403             sync: true,
16404              /**
16405              * @event push
16406              * Fires when the iframe editor is updated with content from the textarea.
16407              * @param {HtmlEditor} this
16408              * @param {String} html
16409              */
16410             push: true,
16411              /**
16412              * @event editmodechange
16413              * Fires when the editor switches edit modes
16414              * @param {HtmlEditor} this
16415              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
16416              */
16417             editmodechange: true,
16418             /**
16419              * @event editorevent
16420              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16421              * @param {HtmlEditor} this
16422              */
16423             editorevent: true,
16424             /**
16425              * @event firstfocus
16426              * Fires when on first focus - needed by toolbars..
16427              * @param {HtmlEditor} this
16428              */
16429             firstfocus: true,
16430             /**
16431              * @event autosave
16432              * Auto save the htmlEditor value as a file into Events
16433              * @param {HtmlEditor} this
16434              */
16435             autosave: true,
16436             /**
16437              * @event savedpreview
16438              * preview the saved version of htmlEditor
16439              * @param {HtmlEditor} this
16440              */
16441             savedpreview: true
16442         });
16443 };
16444
16445
16446 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
16447     
16448     
16449       /**
16450      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
16451      */
16452     toolbars : false,
16453    
16454      /**
16455      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16456      *                        Roo.resizable.
16457      */
16458     resizable : false,
16459      /**
16460      * @cfg {Number} height (in pixels)
16461      */   
16462     height: 300,
16463    /**
16464      * @cfg {Number} width (in pixels)
16465      */   
16466     width: false,
16467     
16468     /**
16469      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16470      * 
16471      */
16472     stylesheets: false,
16473     
16474     // id of frame..
16475     frameId: false,
16476     
16477     // private properties
16478     validationEvent : false,
16479     deferHeight: true,
16480     initialized : false,
16481     activated : false,
16482     
16483     onFocus : Roo.emptyFn,
16484     iframePad:3,
16485     hideMode:'offsets',
16486     
16487     
16488     tbContainer : false,
16489     
16490     toolbarContainer :function() {
16491         return this.wrap.select('.x-html-editor-tb',true).first();
16492     },
16493
16494     /**
16495      * Protected method that will not generally be called directly. It
16496      * is called when the editor creates its toolbar. Override this method if you need to
16497      * add custom toolbar buttons.
16498      * @param {HtmlEditor} editor
16499      */
16500     createToolbar : function(){
16501         
16502         Roo.log("create toolbars");
16503         
16504         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
16505         this.toolbars[0].render(this.toolbarContainer());
16506         
16507         return;
16508         
16509 //        if (!editor.toolbars || !editor.toolbars.length) {
16510 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
16511 //        }
16512 //        
16513 //        for (var i =0 ; i < editor.toolbars.length;i++) {
16514 //            editor.toolbars[i] = Roo.factory(
16515 //                    typeof(editor.toolbars[i]) == 'string' ?
16516 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
16517 //                Roo.bootstrap.HtmlEditor);
16518 //            editor.toolbars[i].init(editor);
16519 //        }
16520     },
16521
16522      
16523     // private
16524     onRender : function(ct, position)
16525     {
16526        // Roo.log("Call onRender: " + this.xtype);
16527         var _t = this;
16528         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
16529       
16530         this.wrap = this.inputEl().wrap({
16531             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
16532         });
16533         
16534         this.editorcore.onRender(ct, position);
16535          
16536         if (this.resizable) {
16537             this.resizeEl = new Roo.Resizable(this.wrap, {
16538                 pinned : true,
16539                 wrap: true,
16540                 dynamic : true,
16541                 minHeight : this.height,
16542                 height: this.height,
16543                 handles : this.resizable,
16544                 width: this.width,
16545                 listeners : {
16546                     resize : function(r, w, h) {
16547                         _t.onResize(w,h); // -something
16548                     }
16549                 }
16550             });
16551             
16552         }
16553         this.createToolbar(this);
16554        
16555         
16556         if(!this.width && this.resizable){
16557             this.setSize(this.wrap.getSize());
16558         }
16559         if (this.resizeEl) {
16560             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
16561             // should trigger onReize..
16562         }
16563         
16564     },
16565
16566     // private
16567     onResize : function(w, h)
16568     {
16569         Roo.log('resize: ' +w + ',' + h );
16570         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
16571         var ew = false;
16572         var eh = false;
16573         
16574         if(this.inputEl() ){
16575             if(typeof w == 'number'){
16576                 var aw = w - this.wrap.getFrameWidth('lr');
16577                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
16578                 ew = aw;
16579             }
16580             if(typeof h == 'number'){
16581                  var tbh = -11;  // fixme it needs to tool bar size!
16582                 for (var i =0; i < this.toolbars.length;i++) {
16583                     // fixme - ask toolbars for heights?
16584                     tbh += this.toolbars[i].el.getHeight();
16585                     //if (this.toolbars[i].footer) {
16586                     //    tbh += this.toolbars[i].footer.el.getHeight();
16587                     //}
16588                 }
16589               
16590                 
16591                 
16592                 
16593                 
16594                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
16595                 ah -= 5; // knock a few pixes off for look..
16596                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
16597                 var eh = ah;
16598             }
16599         }
16600         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
16601         this.editorcore.onResize(ew,eh);
16602         
16603     },
16604
16605     /**
16606      * Toggles the editor between standard and source edit mode.
16607      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16608      */
16609     toggleSourceEdit : function(sourceEditMode)
16610     {
16611         this.editorcore.toggleSourceEdit(sourceEditMode);
16612         
16613         if(this.editorcore.sourceEditMode){
16614             Roo.log('editor - showing textarea');
16615             
16616 //            Roo.log('in');
16617 //            Roo.log(this.syncValue());
16618             this.syncValue();
16619             this.inputEl().removeClass('hide');
16620             this.inputEl().dom.removeAttribute('tabIndex');
16621             this.inputEl().focus();
16622         }else{
16623             Roo.log('editor - hiding textarea');
16624 //            Roo.log('out')
16625 //            Roo.log(this.pushValue()); 
16626             this.pushValue();
16627             
16628             this.inputEl().addClass('hide');
16629             this.inputEl().dom.setAttribute('tabIndex', -1);
16630             //this.deferFocus();
16631         }
16632          
16633         if(this.resizable){
16634             this.setSize(this.wrap.getSize());
16635         }
16636         
16637         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
16638     },
16639  
16640     // private (for BoxComponent)
16641     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16642
16643     // private (for BoxComponent)
16644     getResizeEl : function(){
16645         return this.wrap;
16646     },
16647
16648     // private (for BoxComponent)
16649     getPositionEl : function(){
16650         return this.wrap;
16651     },
16652
16653     // private
16654     initEvents : function(){
16655         this.originalValue = this.getValue();
16656     },
16657
16658 //    /**
16659 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16660 //     * @method
16661 //     */
16662 //    markInvalid : Roo.emptyFn,
16663 //    /**
16664 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16665 //     * @method
16666 //     */
16667 //    clearInvalid : Roo.emptyFn,
16668
16669     setValue : function(v){
16670         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
16671         this.editorcore.pushValue();
16672     },
16673
16674      
16675     // private
16676     deferFocus : function(){
16677         this.focus.defer(10, this);
16678     },
16679
16680     // doc'ed in Field
16681     focus : function(){
16682         this.editorcore.focus();
16683         
16684     },
16685       
16686
16687     // private
16688     onDestroy : function(){
16689         
16690         
16691         
16692         if(this.rendered){
16693             
16694             for (var i =0; i < this.toolbars.length;i++) {
16695                 // fixme - ask toolbars for heights?
16696                 this.toolbars[i].onDestroy();
16697             }
16698             
16699             this.wrap.dom.innerHTML = '';
16700             this.wrap.remove();
16701         }
16702     },
16703
16704     // private
16705     onFirstFocus : function(){
16706         //Roo.log("onFirstFocus");
16707         this.editorcore.onFirstFocus();
16708          for (var i =0; i < this.toolbars.length;i++) {
16709             this.toolbars[i].onFirstFocus();
16710         }
16711         
16712     },
16713     
16714     // private
16715     syncValue : function()
16716     {   
16717         this.editorcore.syncValue();
16718     },
16719     
16720     pushValue : function()
16721     {   
16722         this.editorcore.pushValue();
16723     }
16724      
16725     
16726     // hide stuff that is not compatible
16727     /**
16728      * @event blur
16729      * @hide
16730      */
16731     /**
16732      * @event change
16733      * @hide
16734      */
16735     /**
16736      * @event focus
16737      * @hide
16738      */
16739     /**
16740      * @event specialkey
16741      * @hide
16742      */
16743     /**
16744      * @cfg {String} fieldClass @hide
16745      */
16746     /**
16747      * @cfg {String} focusClass @hide
16748      */
16749     /**
16750      * @cfg {String} autoCreate @hide
16751      */
16752     /**
16753      * @cfg {String} inputType @hide
16754      */
16755     /**
16756      * @cfg {String} invalidClass @hide
16757      */
16758     /**
16759      * @cfg {String} invalidText @hide
16760      */
16761     /**
16762      * @cfg {String} msgFx @hide
16763      */
16764     /**
16765      * @cfg {String} validateOnBlur @hide
16766      */
16767 });
16768  
16769     
16770    
16771    
16772    
16773       
16774 Roo.namespace('Roo.bootstrap.htmleditor');
16775 /**
16776  * @class Roo.bootstrap.HtmlEditorToolbar1
16777  * Basic Toolbar
16778  * 
16779  * Usage:
16780  *
16781  new Roo.bootstrap.HtmlEditor({
16782     ....
16783     toolbars : [
16784         new Roo.bootstrap.HtmlEditorToolbar1({
16785             disable : { fonts: 1 , format: 1, ..., ... , ...],
16786             btns : [ .... ]
16787         })
16788     }
16789      
16790  * 
16791  * @cfg {Object} disable List of elements to disable..
16792  * @cfg {Array} btns List of additional buttons.
16793  * 
16794  * 
16795  * NEEDS Extra CSS? 
16796  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
16797  */
16798  
16799 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
16800 {
16801     
16802     Roo.apply(this, config);
16803     
16804     // default disabled, based on 'good practice'..
16805     this.disable = this.disable || {};
16806     Roo.applyIf(this.disable, {
16807         fontSize : true,
16808         colors : true,
16809         specialElements : true
16810     });
16811     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
16812     
16813     this.editor = config.editor;
16814     this.editorcore = config.editor.editorcore;
16815     
16816     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
16817     
16818     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
16819     // dont call parent... till later.
16820 }
16821 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
16822      
16823     bar : true,
16824     
16825     editor : false,
16826     editorcore : false,
16827     
16828     
16829     formats : [
16830         "p" ,  
16831         "h1","h2","h3","h4","h5","h6", 
16832         "pre", "code", 
16833         "abbr", "acronym", "address", "cite", "samp", "var",
16834         'div','span'
16835     ],
16836     
16837     onRender : function(ct, position)
16838     {
16839        // Roo.log("Call onRender: " + this.xtype);
16840         
16841        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
16842        Roo.log(this.el);
16843        this.el.dom.style.marginBottom = '0';
16844        var _this = this;
16845        var editorcore = this.editorcore;
16846        var editor= this.editor;
16847        
16848        var children = [];
16849        var btn = function(id,cmd , toggle, handler){
16850        
16851             var  event = toggle ? 'toggle' : 'click';
16852        
16853             var a = {
16854                 size : 'sm',
16855                 xtype: 'Button',
16856                 xns: Roo.bootstrap,
16857                 glyphicon : id,
16858                 cmd : id || cmd,
16859                 enableToggle:toggle !== false,
16860                 //html : 'submit'
16861                 pressed : toggle ? false : null,
16862                 listeners : {}
16863             }
16864             a.listeners[toggle ? 'toggle' : 'click'] = function() {
16865                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
16866             }
16867             children.push(a);
16868             return a;
16869        }
16870         
16871         var style = {
16872                 xtype: 'Button',
16873                 size : 'sm',
16874                 xns: Roo.bootstrap,
16875                 glyphicon : 'font',
16876                 //html : 'submit'
16877                 menu : {
16878                     xtype: 'Menu',
16879                     xns: Roo.bootstrap,
16880                     items:  []
16881                 }
16882         };
16883         Roo.each(this.formats, function(f) {
16884             style.menu.items.push({
16885                 xtype :'MenuItem',
16886                 xns: Roo.bootstrap,
16887                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
16888                 tagname : f,
16889                 listeners : {
16890                     click : function()
16891                     {
16892                         editorcore.insertTag(this.tagname);
16893                         editor.focus();
16894                     }
16895                 }
16896                 
16897             });
16898         });
16899          children.push(style);   
16900             
16901             
16902         btn('bold',false,true);
16903         btn('italic',false,true);
16904         btn('align-left', 'justifyleft',true);
16905         btn('align-center', 'justifycenter',true);
16906         btn('align-right' , 'justifyright',true);
16907         btn('link', false, false, function(btn) {
16908             //Roo.log("create link?");
16909             var url = prompt(this.createLinkText, this.defaultLinkValue);
16910             if(url && url != 'http:/'+'/'){
16911                 this.editorcore.relayCmd('createlink', url);
16912             }
16913         }),
16914         btn('list','insertunorderedlist',true);
16915         btn('pencil', false,true, function(btn){
16916                 Roo.log(this);
16917                 
16918                 this.toggleSourceEdit(btn.pressed);
16919         });
16920         /*
16921         var cog = {
16922                 xtype: 'Button',
16923                 size : 'sm',
16924                 xns: Roo.bootstrap,
16925                 glyphicon : 'cog',
16926                 //html : 'submit'
16927                 menu : {
16928                     xtype: 'Menu',
16929                     xns: Roo.bootstrap,
16930                     items:  []
16931                 }
16932         };
16933         
16934         cog.menu.items.push({
16935             xtype :'MenuItem',
16936             xns: Roo.bootstrap,
16937             html : Clean styles,
16938             tagname : f,
16939             listeners : {
16940                 click : function()
16941                 {
16942                     editorcore.insertTag(this.tagname);
16943                     editor.focus();
16944                 }
16945             }
16946             
16947         });
16948        */
16949         
16950          
16951        this.xtype = 'NavSimplebar';
16952         
16953         for(var i=0;i< children.length;i++) {
16954             
16955             this.buttons.add(this.addxtypeChild(children[i]));
16956             
16957         }
16958         
16959         editor.on('editorevent', this.updateToolbar, this);
16960     },
16961     onBtnClick : function(id)
16962     {
16963        this.editorcore.relayCmd(id);
16964        this.editorcore.focus();
16965     },
16966     
16967     /**
16968      * Protected method that will not generally be called directly. It triggers
16969      * a toolbar update by reading the markup state of the current selection in the editor.
16970      */
16971     updateToolbar: function(){
16972
16973         if(!this.editorcore.activated){
16974             this.editor.onFirstFocus(); // is this neeed?
16975             return;
16976         }
16977
16978         var btns = this.buttons; 
16979         var doc = this.editorcore.doc;
16980         btns.get('bold').setActive(doc.queryCommandState('bold'));
16981         btns.get('italic').setActive(doc.queryCommandState('italic'));
16982         //btns.get('underline').setActive(doc.queryCommandState('underline'));
16983         
16984         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
16985         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
16986         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
16987         
16988         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
16989         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
16990          /*
16991         
16992         var ans = this.editorcore.getAllAncestors();
16993         if (this.formatCombo) {
16994             
16995             
16996             var store = this.formatCombo.store;
16997             this.formatCombo.setValue("");
16998             for (var i =0; i < ans.length;i++) {
16999                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
17000                     // select it..
17001                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
17002                     break;
17003                 }
17004             }
17005         }
17006         
17007         
17008         
17009         // hides menus... - so this cant be on a menu...
17010         Roo.bootstrap.MenuMgr.hideAll();
17011         */
17012         Roo.bootstrap.MenuMgr.hideAll();
17013         //this.editorsyncValue();
17014     },
17015     onFirstFocus: function() {
17016         this.buttons.each(function(item){
17017            item.enable();
17018         });
17019     },
17020     toggleSourceEdit : function(sourceEditMode){
17021         
17022           
17023         if(sourceEditMode){
17024             Roo.log("disabling buttons");
17025            this.buttons.each( function(item){
17026                 if(item.cmd != 'pencil'){
17027                     item.disable();
17028                 }
17029             });
17030           
17031         }else{
17032             Roo.log("enabling buttons");
17033             if(this.editorcore.initialized){
17034                 this.buttons.each( function(item){
17035                     item.enable();
17036                 });
17037             }
17038             
17039         }
17040         Roo.log("calling toggole on editor");
17041         // tell the editor that it's been pressed..
17042         this.editor.toggleSourceEdit(sourceEditMode);
17043        
17044     }
17045 });
17046
17047
17048
17049
17050
17051 /**
17052  * @class Roo.bootstrap.Table.AbstractSelectionModel
17053  * @extends Roo.util.Observable
17054  * Abstract base class for grid SelectionModels.  It provides the interface that should be
17055  * implemented by descendant classes.  This class should not be directly instantiated.
17056  * @constructor
17057  */
17058 Roo.bootstrap.Table.AbstractSelectionModel = function(){
17059     this.locked = false;
17060     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
17061 };
17062
17063
17064 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
17065     /** @ignore Called by the grid automatically. Do not call directly. */
17066     init : function(grid){
17067         this.grid = grid;
17068         this.initEvents();
17069     },
17070
17071     /**
17072      * Locks the selections.
17073      */
17074     lock : function(){
17075         this.locked = true;
17076     },
17077
17078     /**
17079      * Unlocks the selections.
17080      */
17081     unlock : function(){
17082         this.locked = false;
17083     },
17084
17085     /**
17086      * Returns true if the selections are locked.
17087      * @return {Boolean}
17088      */
17089     isLocked : function(){
17090         return this.locked;
17091     }
17092 });
17093 /**
17094  * @extends Roo.bootstrap.Table.AbstractSelectionModel
17095  * @class Roo.bootstrap.Table.RowSelectionModel
17096  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
17097  * It supports multiple selections and keyboard selection/navigation. 
17098  * @constructor
17099  * @param {Object} config
17100  */
17101
17102 Roo.bootstrap.Table.RowSelectionModel = function(config){
17103     Roo.apply(this, config);
17104     this.selections = new Roo.util.MixedCollection(false, function(o){
17105         return o.id;
17106     });
17107
17108     this.last = false;
17109     this.lastActive = false;
17110
17111     this.addEvents({
17112         /**
17113              * @event selectionchange
17114              * Fires when the selection changes
17115              * @param {SelectionModel} this
17116              */
17117             "selectionchange" : true,
17118         /**
17119              * @event afterselectionchange
17120              * Fires after the selection changes (eg. by key press or clicking)
17121              * @param {SelectionModel} this
17122              */
17123             "afterselectionchange" : true,
17124         /**
17125              * @event beforerowselect
17126              * Fires when a row is selected being selected, return false to cancel.
17127              * @param {SelectionModel} this
17128              * @param {Number} rowIndex The selected index
17129              * @param {Boolean} keepExisting False if other selections will be cleared
17130              */
17131             "beforerowselect" : true,
17132         /**
17133              * @event rowselect
17134              * Fires when a row is selected.
17135              * @param {SelectionModel} this
17136              * @param {Number} rowIndex The selected index
17137              * @param {Roo.data.Record} r The record
17138              */
17139             "rowselect" : true,
17140         /**
17141              * @event rowdeselect
17142              * Fires when a row is deselected.
17143              * @param {SelectionModel} this
17144              * @param {Number} rowIndex The selected index
17145              */
17146         "rowdeselect" : true
17147     });
17148     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
17149     this.locked = false;
17150 };
17151
17152 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
17153     /**
17154      * @cfg {Boolean} singleSelect
17155      * True to allow selection of only one row at a time (defaults to false)
17156      */
17157     singleSelect : false,
17158
17159     // private
17160     initEvents : function(){
17161
17162         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
17163             this.grid.on("mousedown", this.handleMouseDown, this);
17164         }else{ // allow click to work like normal
17165             this.grid.on("rowclick", this.handleDragableRowClick, this);
17166         }
17167
17168         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
17169             "up" : function(e){
17170                 if(!e.shiftKey){
17171                     this.selectPrevious(e.shiftKey);
17172                 }else if(this.last !== false && this.lastActive !== false){
17173                     var last = this.last;
17174                     this.selectRange(this.last,  this.lastActive-1);
17175                     this.grid.getView().focusRow(this.lastActive);
17176                     if(last !== false){
17177                         this.last = last;
17178                     }
17179                 }else{
17180                     this.selectFirstRow();
17181                 }
17182                 this.fireEvent("afterselectionchange", this);
17183             },
17184             "down" : function(e){
17185                 if(!e.shiftKey){
17186                     this.selectNext(e.shiftKey);
17187                 }else if(this.last !== false && this.lastActive !== false){
17188                     var last = this.last;
17189                     this.selectRange(this.last,  this.lastActive+1);
17190                     this.grid.getView().focusRow(this.lastActive);
17191                     if(last !== false){
17192                         this.last = last;
17193                     }
17194                 }else{
17195                     this.selectFirstRow();
17196                 }
17197                 this.fireEvent("afterselectionchange", this);
17198             },
17199             scope: this
17200         });
17201
17202         var view = this.grid.view;
17203         view.on("refresh", this.onRefresh, this);
17204         view.on("rowupdated", this.onRowUpdated, this);
17205         view.on("rowremoved", this.onRemove, this);
17206     },
17207
17208     // private
17209     onRefresh : function(){
17210         var ds = this.grid.dataSource, i, v = this.grid.view;
17211         var s = this.selections;
17212         s.each(function(r){
17213             if((i = ds.indexOfId(r.id)) != -1){
17214                 v.onRowSelect(i);
17215             }else{
17216                 s.remove(r);
17217             }
17218         });
17219     },
17220
17221     // private
17222     onRemove : function(v, index, r){
17223         this.selections.remove(r);
17224     },
17225
17226     // private
17227     onRowUpdated : function(v, index, r){
17228         if(this.isSelected(r)){
17229             v.onRowSelect(index);
17230         }
17231     },
17232
17233     /**
17234      * Select records.
17235      * @param {Array} records The records to select
17236      * @param {Boolean} keepExisting (optional) True to keep existing selections
17237      */
17238     selectRecords : function(records, keepExisting){
17239         if(!keepExisting){
17240             this.clearSelections();
17241         }
17242         var ds = this.grid.dataSource;
17243         for(var i = 0, len = records.length; i < len; i++){
17244             this.selectRow(ds.indexOf(records[i]), true);
17245         }
17246     },
17247
17248     /**
17249      * Gets the number of selected rows.
17250      * @return {Number}
17251      */
17252     getCount : function(){
17253         return this.selections.length;
17254     },
17255
17256     /**
17257      * Selects the first row in the grid.
17258      */
17259     selectFirstRow : function(){
17260         this.selectRow(0);
17261     },
17262
17263     /**
17264      * Select the last row.
17265      * @param {Boolean} keepExisting (optional) True to keep existing selections
17266      */
17267     selectLastRow : function(keepExisting){
17268         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
17269     },
17270
17271     /**
17272      * Selects the row immediately following the last selected row.
17273      * @param {Boolean} keepExisting (optional) True to keep existing selections
17274      */
17275     selectNext : function(keepExisting){
17276         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
17277             this.selectRow(this.last+1, keepExisting);
17278             this.grid.getView().focusRow(this.last);
17279         }
17280     },
17281
17282     /**
17283      * Selects the row that precedes the last selected row.
17284      * @param {Boolean} keepExisting (optional) True to keep existing selections
17285      */
17286     selectPrevious : function(keepExisting){
17287         if(this.last){
17288             this.selectRow(this.last-1, keepExisting);
17289             this.grid.getView().focusRow(this.last);
17290         }
17291     },
17292
17293     /**
17294      * Returns the selected records
17295      * @return {Array} Array of selected records
17296      */
17297     getSelections : function(){
17298         return [].concat(this.selections.items);
17299     },
17300
17301     /**
17302      * Returns the first selected record.
17303      * @return {Record}
17304      */
17305     getSelected : function(){
17306         return this.selections.itemAt(0);
17307     },
17308
17309
17310     /**
17311      * Clears all selections.
17312      */
17313     clearSelections : function(fast){
17314         if(this.locked) return;
17315         if(fast !== true){
17316             var ds = this.grid.dataSource;
17317             var s = this.selections;
17318             s.each(function(r){
17319                 this.deselectRow(ds.indexOfId(r.id));
17320             }, this);
17321             s.clear();
17322         }else{
17323             this.selections.clear();
17324         }
17325         this.last = false;
17326     },
17327
17328
17329     /**
17330      * Selects all rows.
17331      */
17332     selectAll : function(){
17333         if(this.locked) return;
17334         this.selections.clear();
17335         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
17336             this.selectRow(i, true);
17337         }
17338     },
17339
17340     /**
17341      * Returns True if there is a selection.
17342      * @return {Boolean}
17343      */
17344     hasSelection : function(){
17345         return this.selections.length > 0;
17346     },
17347
17348     /**
17349      * Returns True if the specified row is selected.
17350      * @param {Number/Record} record The record or index of the record to check
17351      * @return {Boolean}
17352      */
17353     isSelected : function(index){
17354         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
17355         return (r && this.selections.key(r.id) ? true : false);
17356     },
17357
17358     /**
17359      * Returns True if the specified record id is selected.
17360      * @param {String} id The id of record to check
17361      * @return {Boolean}
17362      */
17363     isIdSelected : function(id){
17364         return (this.selections.key(id) ? true : false);
17365     },
17366
17367     // private
17368     handleMouseDown : function(e, t){
17369         var view = this.grid.getView(), rowIndex;
17370         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
17371             return;
17372         };
17373         if(e.shiftKey && this.last !== false){
17374             var last = this.last;
17375             this.selectRange(last, rowIndex, e.ctrlKey);
17376             this.last = last; // reset the last
17377             view.focusRow(rowIndex);
17378         }else{
17379             var isSelected = this.isSelected(rowIndex);
17380             if(e.button !== 0 && isSelected){
17381                 view.focusRow(rowIndex);
17382             }else if(e.ctrlKey && isSelected){
17383                 this.deselectRow(rowIndex);
17384             }else if(!isSelected){
17385                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
17386                 view.focusRow(rowIndex);
17387             }
17388         }
17389         this.fireEvent("afterselectionchange", this);
17390     },
17391     // private
17392     handleDragableRowClick :  function(grid, rowIndex, e) 
17393     {
17394         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
17395             this.selectRow(rowIndex, false);
17396             grid.view.focusRow(rowIndex);
17397              this.fireEvent("afterselectionchange", this);
17398         }
17399     },
17400     
17401     /**
17402      * Selects multiple rows.
17403      * @param {Array} rows Array of the indexes of the row to select
17404      * @param {Boolean} keepExisting (optional) True to keep existing selections
17405      */
17406     selectRows : function(rows, keepExisting){
17407         if(!keepExisting){
17408             this.clearSelections();
17409         }
17410         for(var i = 0, len = rows.length; i < len; i++){
17411             this.selectRow(rows[i], true);
17412         }
17413     },
17414
17415     /**
17416      * Selects a range of rows. All rows in between startRow and endRow are also selected.
17417      * @param {Number} startRow The index of the first row in the range
17418      * @param {Number} endRow The index of the last row in the range
17419      * @param {Boolean} keepExisting (optional) True to retain existing selections
17420      */
17421     selectRange : function(startRow, endRow, keepExisting){
17422         if(this.locked) return;
17423         if(!keepExisting){
17424             this.clearSelections();
17425         }
17426         if(startRow <= endRow){
17427             for(var i = startRow; i <= endRow; i++){
17428                 this.selectRow(i, true);
17429             }
17430         }else{
17431             for(var i = startRow; i >= endRow; i--){
17432                 this.selectRow(i, true);
17433             }
17434         }
17435     },
17436
17437     /**
17438      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
17439      * @param {Number} startRow The index of the first row in the range
17440      * @param {Number} endRow The index of the last row in the range
17441      */
17442     deselectRange : function(startRow, endRow, preventViewNotify){
17443         if(this.locked) return;
17444         for(var i = startRow; i <= endRow; i++){
17445             this.deselectRow(i, preventViewNotify);
17446         }
17447     },
17448
17449     /**
17450      * Selects a row.
17451      * @param {Number} row The index of the row to select
17452      * @param {Boolean} keepExisting (optional) True to keep existing selections
17453      */
17454     selectRow : function(index, keepExisting, preventViewNotify){
17455         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
17456         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
17457             if(!keepExisting || this.singleSelect){
17458                 this.clearSelections();
17459             }
17460             var r = this.grid.dataSource.getAt(index);
17461             this.selections.add(r);
17462             this.last = this.lastActive = index;
17463             if(!preventViewNotify){
17464                 this.grid.getView().onRowSelect(index);
17465             }
17466             this.fireEvent("rowselect", this, index, r);
17467             this.fireEvent("selectionchange", this);
17468         }
17469     },
17470
17471     /**
17472      * Deselects a row.
17473      * @param {Number} row The index of the row to deselect
17474      */
17475     deselectRow : function(index, preventViewNotify){
17476         if(this.locked) return;
17477         if(this.last == index){
17478             this.last = false;
17479         }
17480         if(this.lastActive == index){
17481             this.lastActive = false;
17482         }
17483         var r = this.grid.dataSource.getAt(index);
17484         this.selections.remove(r);
17485         if(!preventViewNotify){
17486             this.grid.getView().onRowDeselect(index);
17487         }
17488         this.fireEvent("rowdeselect", this, index);
17489         this.fireEvent("selectionchange", this);
17490     },
17491
17492     // private
17493     restoreLast : function(){
17494         if(this._last){
17495             this.last = this._last;
17496         }
17497     },
17498
17499     // private
17500     acceptsNav : function(row, col, cm){
17501         return !cm.isHidden(col) && cm.isCellEditable(col, row);
17502     },
17503
17504     // private
17505     onEditorKey : function(field, e){
17506         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
17507         if(k == e.TAB){
17508             e.stopEvent();
17509             ed.completeEdit();
17510             if(e.shiftKey){
17511                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
17512             }else{
17513                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
17514             }
17515         }else if(k == e.ENTER && !e.ctrlKey){
17516             e.stopEvent();
17517             ed.completeEdit();
17518             if(e.shiftKey){
17519                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
17520             }else{
17521                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
17522             }
17523         }else if(k == e.ESC){
17524             ed.cancelEdit();
17525         }
17526         if(newCell){
17527             g.startEditing(newCell[0], newCell[1]);
17528         }
17529     }
17530 });/*
17531  * Based on:
17532  * Ext JS Library 1.1.1
17533  * Copyright(c) 2006-2007, Ext JS, LLC.
17534  *
17535  * Originally Released Under LGPL - original licence link has changed is not relivant.
17536  *
17537  * Fork - LGPL
17538  * <script type="text/javascript">
17539  */
17540  
17541 /**
17542  * @class Roo.bootstrap.PagingToolbar
17543  * @extends Roo.Row
17544  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
17545  * @constructor
17546  * Create a new PagingToolbar
17547  * @param {Object} config The config object
17548  */
17549 Roo.bootstrap.PagingToolbar = function(config)
17550 {
17551     // old args format still supported... - xtype is prefered..
17552         // created from xtype...
17553     var ds = config.dataSource;
17554     var items = [];
17555     if (config.items) {
17556         items = config.items;
17557         config.items = [];
17558     }
17559     
17560     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
17561     this.ds = ds;
17562     this.cursor = 0;
17563     if (ds) { 
17564         this.bind(ds);
17565     }
17566     
17567     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
17568     
17569     // supprot items array.
17570     
17571     Roo.each(items, function(e) {
17572         this.add(Roo.factory(e));
17573     },this);
17574     
17575     
17576     
17577     
17578     
17579 };
17580
17581 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
17582     /**
17583      * @cfg {Roo.data.Store} dataSource
17584      * The underlying data store providing the paged data
17585      */
17586     /**
17587      * @cfg {String/HTMLElement/Element} container
17588      * container The id or element that will contain the toolbar
17589      */
17590     /**
17591      * @cfg {Boolean} displayInfo
17592      * True to display the displayMsg (defaults to false)
17593      */
17594     /**
17595      * @cfg {Number} pageSize
17596      * The number of records to display per page (defaults to 20)
17597      */
17598     pageSize: 20,
17599     /**
17600      * @cfg {String} displayMsg
17601      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
17602      */
17603     displayMsg : 'Displaying {0} - {1} of {2}',
17604     /**
17605      * @cfg {String} emptyMsg
17606      * The message to display when no records are found (defaults to "No data to display")
17607      */
17608     emptyMsg : 'No data to display',
17609     /**
17610      * Customizable piece of the default paging text (defaults to "Page")
17611      * @type String
17612      */
17613     beforePageText : "Page",
17614     /**
17615      * Customizable piece of the default paging text (defaults to "of %0")
17616      * @type String
17617      */
17618     afterPageText : "of {0}",
17619     /**
17620      * Customizable piece of the default paging text (defaults to "First Page")
17621      * @type String
17622      */
17623     firstText : "First Page",
17624     /**
17625      * Customizable piece of the default paging text (defaults to "Previous Page")
17626      * @type String
17627      */
17628     prevText : "Previous Page",
17629     /**
17630      * Customizable piece of the default paging text (defaults to "Next Page")
17631      * @type String
17632      */
17633     nextText : "Next Page",
17634     /**
17635      * Customizable piece of the default paging text (defaults to "Last Page")
17636      * @type String
17637      */
17638     lastText : "Last Page",
17639     /**
17640      * Customizable piece of the default paging text (defaults to "Refresh")
17641      * @type String
17642      */
17643     refreshText : "Refresh",
17644
17645     // private
17646     onRender : function(ct, position) 
17647     {
17648         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
17649         this.navgroup.parentId = this.id;
17650         this.navgroup.onRender(this.el, null);
17651         // add the buttons to the navgroup
17652         
17653         this.first = this.navgroup.addItem({
17654             tooltip: this.firstText,
17655             cls: "prev",
17656             icon : 'fa fa-backward',
17657             disabled: true,
17658             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
17659         });
17660         
17661         this.prev =  this.navgroup.addItem({
17662             tooltip: this.prevText,
17663             cls: "prev",
17664             icon : 'fa fa-step-backward',
17665             disabled: true,
17666             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
17667         });
17668     //this.addSeparator();
17669         
17670         
17671         var field = this.navgroup.addItem( {
17672             tagtype : 'span',
17673             cls : 'x-paging-position',
17674             
17675             html : this.beforePageText  +
17676                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
17677                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
17678          } ); //?? escaped?
17679         
17680         this.field = field.el.select('input', true).first();
17681         this.field.on("keydown", this.onPagingKeydown, this);
17682         this.field.on("focus", function(){this.dom.select();});
17683     
17684     
17685         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
17686         //this.field.setHeight(18);
17687         //this.addSeparator();
17688         this.next = this.navgroup.addItem({
17689             tooltip: this.nextText,
17690             cls: "next",
17691             html : ' <i class="fa fa-step-forward">',
17692             disabled: true,
17693             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
17694         });
17695         this.last = this.navgroup.addItem({
17696             tooltip: this.lastText,
17697             icon : 'fa fa-forward',
17698             cls: "next",
17699             disabled: true,
17700             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
17701         });
17702     //this.addSeparator();
17703         this.loading = this.navgroup.addItem({
17704             tooltip: this.refreshText,
17705             icon: 'fa fa-refresh',
17706             
17707             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
17708         });
17709
17710         if(this.displayInfo){
17711             var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info' } );
17712             this.displayEl = navel.el.select('a',true).first();
17713         }
17714     
17715     },
17716
17717     // private
17718     updateInfo : function(){
17719         if(this.displayEl){
17720             var count = this.ds.getCount();
17721             var msg = count == 0 ?
17722                 this.emptyMsg :
17723                 String.format(
17724                     this.displayMsg,
17725                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
17726                 );
17727             this.displayEl.update(msg);
17728         }
17729     },
17730
17731     // private
17732     onLoad : function(ds, r, o){
17733        this.cursor = o.params ? o.params.start : 0;
17734        var d = this.getPageData(),
17735             ap = d.activePage,
17736             ps = d.pages;
17737         
17738        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
17739        this.field.dom.value = ap;
17740        this.first.setDisabled(ap == 1);
17741        this.prev.setDisabled(ap == 1);
17742        this.next.setDisabled(ap == ps);
17743        this.last.setDisabled(ap == ps);
17744        this.loading.enable();
17745        this.updateInfo();
17746     },
17747
17748     // private
17749     getPageData : function(){
17750         var total = this.ds.getTotalCount();
17751         return {
17752             total : total,
17753             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
17754             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
17755         };
17756     },
17757
17758     // private
17759     onLoadError : function(){
17760         this.loading.enable();
17761     },
17762
17763     // private
17764     onPagingKeydown : function(e){
17765         var k = e.getKey();
17766         var d = this.getPageData();
17767         if(k == e.RETURN){
17768             var v = this.field.dom.value, pageNum;
17769             if(!v || isNaN(pageNum = parseInt(v, 10))){
17770                 this.field.dom.value = d.activePage;
17771                 return;
17772             }
17773             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
17774             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
17775             e.stopEvent();
17776         }
17777         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))
17778         {
17779           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
17780           this.field.dom.value = pageNum;
17781           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
17782           e.stopEvent();
17783         }
17784         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
17785         {
17786           var v = this.field.dom.value, pageNum; 
17787           var increment = (e.shiftKey) ? 10 : 1;
17788           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
17789             increment *= -1;
17790           if(!v || isNaN(pageNum = parseInt(v, 10))) {
17791             this.field.dom.value = d.activePage;
17792             return;
17793           }
17794           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
17795           {
17796             this.field.dom.value = parseInt(v, 10) + increment;
17797             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
17798             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
17799           }
17800           e.stopEvent();
17801         }
17802     },
17803
17804     // private
17805     beforeLoad : function(){
17806         if(this.loading){
17807             this.loading.disable();
17808         }
17809     },
17810
17811     // private
17812     onClick : function(which){
17813         var ds = this.ds;
17814         if (!ds) {
17815             return;
17816         }
17817         switch(which){
17818             case "first":
17819                 ds.load({params:{start: 0, limit: this.pageSize}});
17820             break;
17821             case "prev":
17822                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
17823             break;
17824             case "next":
17825                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
17826             break;
17827             case "last":
17828                 var total = ds.getTotalCount();
17829                 var extra = total % this.pageSize;
17830                 var lastStart = extra ? (total - extra) : total-this.pageSize;
17831                 ds.load({params:{start: lastStart, limit: this.pageSize}});
17832             break;
17833             case "refresh":
17834                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
17835             break;
17836         }
17837     },
17838
17839     /**
17840      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
17841      * @param {Roo.data.Store} store The data store to unbind
17842      */
17843     unbind : function(ds){
17844         ds.un("beforeload", this.beforeLoad, this);
17845         ds.un("load", this.onLoad, this);
17846         ds.un("loadexception", this.onLoadError, this);
17847         ds.un("remove", this.updateInfo, this);
17848         ds.un("add", this.updateInfo, this);
17849         this.ds = undefined;
17850     },
17851
17852     /**
17853      * Binds the paging toolbar to the specified {@link Roo.data.Store}
17854      * @param {Roo.data.Store} store The data store to bind
17855      */
17856     bind : function(ds){
17857         ds.on("beforeload", this.beforeLoad, this);
17858         ds.on("load", this.onLoad, this);
17859         ds.on("loadexception", this.onLoadError, this);
17860         ds.on("remove", this.updateInfo, this);
17861         ds.on("add", this.updateInfo, this);
17862         this.ds = ds;
17863     }
17864 });/*
17865  * - LGPL
17866  *
17867  * element
17868  * 
17869  */
17870
17871 /**
17872  * @class Roo.bootstrap.MessageBar
17873  * @extends Roo.bootstrap.Component
17874  * Bootstrap MessageBar class
17875  * @cfg {String} html contents of the MessageBar
17876  * @cfg {String} weight (info | success | warning | danger) default info
17877  * @cfg {String} beforeClass insert the bar before the given class
17878  * @cfg {Boolean} closable (true | false) default false
17879  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
17880  * 
17881  * @constructor
17882  * Create a new Element
17883  * @param {Object} config The config object
17884  */
17885
17886 Roo.bootstrap.MessageBar = function(config){
17887     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
17888 };
17889
17890 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
17891     
17892     html: '',
17893     weight: 'info',
17894     closable: false,
17895     fixed: false,
17896     beforeClass: 'bootstrap-sticky-wrap',
17897     
17898     getAutoCreate : function(){
17899         
17900         var cfg = {
17901             tag: 'div',
17902             cls: 'alert alert-dismissable alert-' + this.weight,
17903             cn: [
17904                 {
17905                     tag: 'span',
17906                     cls: 'message',
17907                     html: this.html || ''
17908                 }
17909             ]
17910         }
17911         
17912         if(this.fixed){
17913             cfg.cls += ' alert-messages-fixed';
17914         }
17915         
17916         if(this.closable){
17917             cfg.cn.push({
17918                 tag: 'button',
17919                 cls: 'close',
17920                 html: 'x'
17921             });
17922         }
17923         
17924         return cfg;
17925     },
17926     
17927     onRender : function(ct, position)
17928     {
17929         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17930         
17931         if(!this.el){
17932             var cfg = Roo.apply({},  this.getAutoCreate());
17933             cfg.id = Roo.id();
17934             
17935             if (this.cls) {
17936                 cfg.cls += ' ' + this.cls;
17937             }
17938             if (this.style) {
17939                 cfg.style = this.style;
17940             }
17941             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
17942             
17943             this.el.setVisibilityMode(Roo.Element.DISPLAY);
17944         }
17945         
17946         this.el.select('>button.close').on('click', this.hide, this);
17947         
17948     },
17949     
17950     show : function()
17951     {
17952         if (!this.rendered) {
17953             this.render();
17954         }
17955         
17956         this.el.show();
17957         
17958         this.fireEvent('show', this);
17959         
17960     },
17961     
17962     hide : function()
17963     {
17964         if (!this.rendered) {
17965             this.render();
17966         }
17967         
17968         this.el.hide();
17969         
17970         this.fireEvent('hide', this);
17971     },
17972     
17973     update : function()
17974     {
17975 //        var e = this.el.dom.firstChild;
17976 //        
17977 //        if(this.closable){
17978 //            e = e.nextSibling;
17979 //        }
17980 //        
17981 //        e.data = this.html || '';
17982
17983         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
17984     }
17985    
17986 });
17987
17988  
17989
17990