commit
[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  * - LGPL
3831  *
3832  * table
3833  * 
3834  */
3835
3836 /**
3837  * @class Roo.bootstrap.Table
3838  * @extends Roo.bootstrap.Component
3839  * Bootstrap Table class
3840  * @cfg {String} cls table class
3841  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
3842  * @cfg {String} bgcolor Specifies the background color for a table
3843  * @cfg {Number} border Specifies whether the table cells should have borders or not
3844  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
3845  * @cfg {Number} cellspacing Specifies the space between cells
3846  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
3847  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
3848  * @cfg {String} sortable Specifies that the table should be sortable
3849  * @cfg {String} summary Specifies a summary of the content of a table
3850  * @cfg {Number} width Specifies the width of a table
3851  * 
3852  * @cfg {boolean} striped Should the rows be alternative striped
3853  * @cfg {boolean} bordered Add borders to the table
3854  * @cfg {boolean} hover Add hover highlighting
3855  * @cfg {boolean} condensed Format condensed
3856  * @cfg {boolean} responsive Format condensed
3857  * @cfg {Boolean} loadMask (true|false) default false
3858  *
3859  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
3860  
3861  * 
3862  * @constructor
3863  * Create a new Table
3864  * @param {Object} config The config object
3865  */
3866
3867 Roo.bootstrap.Table = function(config){
3868     Roo.bootstrap.Table.superclass.constructor.call(this, config);
3869     
3870     if (this.sm) {
3871         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
3872         this.sm = this.selModel;
3873         this.sm.xmodule = this.xmodule || false;
3874     }
3875     if (this.cm && typeof(this.cm.config) == 'undefined') {
3876         this.colModel = new Roo.grid.ColumnModel(this.cm);
3877         this.cm = this.colModel;
3878         this.cm.xmodule = this.xmodule || false;
3879     }
3880     if (this.store) {
3881         this.store= Roo.factory(this.store, Roo.data);
3882         this.ds = this.store;
3883         this.ds.xmodule = this.xmodule || false;
3884          
3885     }
3886     if (this.footer && this.store) {
3887         this.footer.dataSource = this.ds;
3888         this.footer = Roo.factory(this.footer);
3889     }
3890 };
3891
3892 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
3893     
3894     cls: false,
3895     align: false,
3896     bgcolor: false,
3897     border: false,
3898     cellpadding: false,
3899     cellspacing: false,
3900     frame: false,
3901     rules: false,
3902     sortable: false,
3903     summary: false,
3904     width: false,
3905     striped : false,
3906     bordered: false,
3907     hover:  false,
3908     condensed : false,
3909     responsive : false,
3910     sm : false,
3911     cm : false,
3912     store : false,
3913     loadMask : false,
3914     
3915     getAutoCreate : function(){
3916         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
3917         
3918         cfg = {
3919             tag: 'table',
3920             cls : 'table',
3921             cn : []
3922         }
3923             
3924         if (this.striped) {
3925             cfg.cls += ' table-striped';
3926         }
3927         if (this.hover) {
3928             cfg.cls += ' table-hover';
3929         }
3930         if (this.bordered) {
3931             cfg.cls += ' table-bordered';
3932         }
3933         if (this.condensed) {
3934             cfg.cls += ' table-condensed';
3935         }
3936         if (this.responsive) {
3937             cfg.cls += ' table-responsive';
3938         }
3939         
3940           
3941         
3942         
3943         if (this.cls) {
3944             cfg.cls+=  ' ' +this.cls;
3945         }
3946         
3947         // this lot should be simplifed...
3948         
3949         if (this.align) {
3950             cfg.align=this.align;
3951         }
3952         if (this.bgcolor) {
3953             cfg.bgcolor=this.bgcolor;
3954         }
3955         if (this.border) {
3956             cfg.border=this.border;
3957         }
3958         if (this.cellpadding) {
3959             cfg.cellpadding=this.cellpadding;
3960         }
3961         if (this.cellspacing) {
3962             cfg.cellspacing=this.cellspacing;
3963         }
3964         if (this.frame) {
3965             cfg.frame=this.frame;
3966         }
3967         if (this.rules) {
3968             cfg.rules=this.rules;
3969         }
3970         if (this.sortable) {
3971             cfg.sortable=this.sortable;
3972         }
3973         if (this.summary) {
3974             cfg.summary=this.summary;
3975         }
3976         if (this.width) {
3977             cfg.width=this.width;
3978         }
3979         
3980         if(this.store || this.cm){
3981             cfg.cn.push(this.renderHeader());
3982             cfg.cn.push(this.renderBody());
3983             cfg.cn.push(this.renderFooter());
3984             
3985             cfg.cls+=  ' TableGrid';
3986         }
3987         
3988         return cfg;
3989     },
3990 //    
3991 //    initTableGrid : function()
3992 //    {
3993 //        var cfg = {};
3994 //        
3995 //        var header = {
3996 //            tag: 'thead',
3997 //            cn : []
3998 //        };
3999 //        
4000 //        var cm = this.cm;
4001 //        
4002 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4003 //            header.cn.push({
4004 //                tag: 'th',
4005 //                html: cm.getColumnHeader(i)
4006 //            })
4007 //        }
4008 //        
4009 //        cfg.push(header);
4010 //        
4011 //        return cfg;
4012 //        
4013 //        
4014 //    },
4015     
4016     initEvents : function()
4017     {   
4018         if(!this.store || !this.cm){
4019             return;
4020         }
4021         
4022         Roo.log('initEvents with ds!!!!');
4023         
4024         var _this = this;
4025         
4026         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4027             e.on('click', _this.sort, _this);
4028         });
4029 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
4030 //        this.maskEl.enableDisplayMode("block");
4031 //        this.maskEl.show();
4032         
4033         this.parent().el.setStyle('position', 'relative');
4034         if (this.footer) {
4035             this.footer.parentId = this.id;
4036             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
4037         }
4038         
4039         
4040         // mask should be using Roo.bootstrap.Mask...
4041         
4042         var mark = {
4043             tag: "div",
4044             cls:"x-dlg-mask",
4045             style: "text-align:center",
4046             cn: [
4047                 {
4048                     tag: "div",
4049                     style: "background-color:white;width:50%;margin:100 auto",
4050                     cn: [
4051                         {
4052                             tag: "img",
4053                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
4054                         },
4055                         {
4056                             tag: "span",
4057                             html: "Loading"
4058                         }
4059                         
4060                     ]
4061                 }
4062             ]
4063         }
4064         this.maskEl = Roo.DomHelper.append(this.parent().el, mark, true);
4065         
4066         var size = this.parent().el.getSize();
4067         
4068         this.maskEl.setSize(size.width, 300); // we will fix the height at the beginning...
4069         
4070         this.maskEl.enableDisplayMode("block");
4071         
4072         this.store.on('load', this.onLoad, this);
4073         this.store.on('beforeload', this.onBeforeLoad, this);
4074         
4075         // load should be trigger on render..
4076         //this.store.load();
4077         
4078         
4079         
4080     },
4081     
4082     sort : function(e,el)
4083     {
4084         var col = Roo.get(el)
4085         
4086         if(!col.hasClass('sortable')){
4087             return;
4088         }
4089         
4090         var sort = col.attr('sort');
4091         var dir = 'ASC';
4092         
4093         if(col.hasClass('glyphicon-arrow-up')){
4094             dir = 'DESC';
4095         }
4096         
4097         this.store.sortInfo = {field : sort, direction : dir};
4098         
4099         this.store.load();
4100     },
4101     
4102     renderHeader : function()
4103     {
4104         var header = {
4105             tag: 'thead',
4106             cn : []
4107         };
4108         
4109         var cm = this.cm;
4110         
4111         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4112             
4113             var config = cm.config[i];
4114             
4115             if(typeof(config.hidden) != 'undefined' && config.hidden){
4116                 continue;
4117             }
4118                     
4119             var c = {
4120                 tag: 'th',
4121                 html: cm.getColumnHeader(i)
4122             };
4123             
4124             if(typeof(config.dataIndex) != 'undefined'){
4125                 c.sort = config.dataIndex;
4126             }
4127             
4128             if(typeof(config.sortable) != 'undefined' && config.sortable){
4129                 c.cls = 'sortable';
4130             }
4131             
4132             if(typeof(config.width) != 'undefined'){
4133                 c.style = 'width:' + config.width + 'px';
4134             }
4135             
4136             header.cn.push(c)
4137         }
4138         
4139         return header;
4140     },
4141     
4142     renderBody : function()
4143     {
4144         var body = {
4145             tag: 'tbody',
4146             cn : []
4147         };
4148         
4149         return body;
4150     },
4151     
4152     renderFooter : function()
4153     {
4154         var footer = {
4155             tag: 'tfoot',
4156             cn : [
4157                 {
4158                     tag: 'tr',
4159                     cn : [
4160                         {
4161                             tag : 'td',
4162                             colspan : 1
4163                         }
4164                     ]
4165                 }
4166             ]
4167         };
4168         
4169         return footer;
4170     },
4171     
4172     onLoad : function()
4173     {
4174         Roo.log('ds onload');
4175         
4176         var _this = this;
4177         var cm = this.cm;
4178         
4179         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4180             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
4181             
4182             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
4183                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
4184             }
4185             
4186             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
4187                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
4188             }
4189         });
4190         
4191         var tbody = this.el.select('tbody', true).first();
4192         
4193         var renders = [];
4194                     
4195         if(this.store.getCount() > 0){
4196             this.store.data.each(function(d){
4197                 var row = {
4198                     tag : 'tr',
4199                     cn : []
4200                 };
4201                 
4202                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4203                     var config = cm.config[i];
4204                     
4205                     if(typeof(config.hidden) != 'undefined' && config.hidden){
4206                         continue;
4207                     }
4208                     
4209                     var renderer = cm.getRenderer(i);
4210                     var value = '';
4211                     var id = Roo.id();
4212                     
4213                     if(typeof(renderer) !== 'undefined'){
4214                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
4215                     }
4216                     
4217                     if(typeof(value) === 'object'){
4218                         renders.push({
4219                             id : id,
4220                             cfg : value 
4221                         })
4222                     }
4223                     
4224                     var td = {
4225                         tag: 'td',
4226                         id: id,
4227                         html: (typeof(value) === 'object') ? '' : value
4228                     };
4229                     
4230                     if(typeof(config.width) != 'undefined'){
4231                         td.style = 'width:' +  config.width + 'px';
4232                     }
4233                     
4234                     row.cn.push(td);
4235                    
4236                 }
4237                 
4238                 tbody.createChild(row);
4239                 
4240             });
4241         }
4242         
4243         
4244         if(renders.length){
4245             var _this = this;
4246             Roo.each(renders, function(r){
4247                 _this.renderColumn(r);
4248             })
4249         }
4250
4251         if(this.loadMask){
4252             this.maskEl.hide();
4253         }
4254     },
4255     
4256     onBeforeLoad : function()
4257     {
4258         Roo.log('ds onBeforeLoad');
4259         
4260         this.clear();
4261         
4262         if(this.loadMask){
4263             this.maskEl.show();
4264         }
4265     },
4266     
4267     clear : function()
4268     {
4269         this.el.select('tbody', true).first().dom.innerHTML = '';
4270     },
4271     
4272     getSelectionModel : function(){
4273         if(!this.selModel){
4274             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
4275         }
4276         return this.selModel;
4277     },
4278     
4279     renderColumn : function(r)
4280     {
4281         var _this = this;
4282         r.cfg.render(Roo.get(r.id));
4283         
4284         if(r.cfg.cn){
4285             Roo.each(r.cfg.cn, function(c){
4286                 var child = {
4287                     id: r.id,
4288                     cfg: c
4289                 }
4290                 _this.renderColumn(child);
4291             })
4292         }
4293     }
4294    
4295 });
4296
4297  
4298
4299  /*
4300  * - LGPL
4301  *
4302  * table cell
4303  * 
4304  */
4305
4306 /**
4307  * @class Roo.bootstrap.TableCell
4308  * @extends Roo.bootstrap.Component
4309  * Bootstrap TableCell class
4310  * @cfg {String} html cell contain text
4311  * @cfg {String} cls cell class
4312  * @cfg {String} tag cell tag (td|th) default td
4313  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
4314  * @cfg {String} align Aligns the content in a cell
4315  * @cfg {String} axis Categorizes cells
4316  * @cfg {String} bgcolor Specifies the background color of a cell
4317  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4318  * @cfg {Number} colspan Specifies the number of columns a cell should span
4319  * @cfg {String} headers Specifies one or more header cells a cell is related to
4320  * @cfg {Number} height Sets the height of a cell
4321  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
4322  * @cfg {Number} rowspan Sets the number of rows a cell should span
4323  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
4324  * @cfg {String} valign Vertical aligns the content in a cell
4325  * @cfg {Number} width Specifies the width of a cell
4326  * 
4327  * @constructor
4328  * Create a new TableCell
4329  * @param {Object} config The config object
4330  */
4331
4332 Roo.bootstrap.TableCell = function(config){
4333     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
4334 };
4335
4336 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
4337     
4338     html: false,
4339     cls: false,
4340     tag: false,
4341     abbr: false,
4342     align: false,
4343     axis: false,
4344     bgcolor: false,
4345     charoff: false,
4346     colspan: false,
4347     headers: false,
4348     height: false,
4349     nowrap: false,
4350     rowspan: false,
4351     scope: false,
4352     valign: false,
4353     width: false,
4354     
4355     
4356     getAutoCreate : function(){
4357         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
4358         
4359         cfg = {
4360             tag: 'td'
4361         }
4362         
4363         if(this.tag){
4364             cfg.tag = this.tag;
4365         }
4366         
4367         if (this.html) {
4368             cfg.html=this.html
4369         }
4370         if (this.cls) {
4371             cfg.cls=this.cls
4372         }
4373         if (this.abbr) {
4374             cfg.abbr=this.abbr
4375         }
4376         if (this.align) {
4377             cfg.align=this.align
4378         }
4379         if (this.axis) {
4380             cfg.axis=this.axis
4381         }
4382         if (this.bgcolor) {
4383             cfg.bgcolor=this.bgcolor
4384         }
4385         if (this.charoff) {
4386             cfg.charoff=this.charoff
4387         }
4388         if (this.colspan) {
4389             cfg.colspan=this.colspan
4390         }
4391         if (this.headers) {
4392             cfg.headers=this.headers
4393         }
4394         if (this.height) {
4395             cfg.height=this.height
4396         }
4397         if (this.nowrap) {
4398             cfg.nowrap=this.nowrap
4399         }
4400         if (this.rowspan) {
4401             cfg.rowspan=this.rowspan
4402         }
4403         if (this.scope) {
4404             cfg.scope=this.scope
4405         }
4406         if (this.valign) {
4407             cfg.valign=this.valign
4408         }
4409         if (this.width) {
4410             cfg.width=this.width
4411         }
4412         
4413         
4414         return cfg;
4415     }
4416    
4417 });
4418
4419  
4420
4421  /*
4422  * - LGPL
4423  *
4424  * table row
4425  * 
4426  */
4427
4428 /**
4429  * @class Roo.bootstrap.TableRow
4430  * @extends Roo.bootstrap.Component
4431  * Bootstrap TableRow class
4432  * @cfg {String} cls row class
4433  * @cfg {String} align Aligns the content in a table row
4434  * @cfg {String} bgcolor Specifies a background color for a table row
4435  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4436  * @cfg {String} valign Vertical aligns the content in a table row
4437  * 
4438  * @constructor
4439  * Create a new TableRow
4440  * @param {Object} config The config object
4441  */
4442
4443 Roo.bootstrap.TableRow = function(config){
4444     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4445 };
4446
4447 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
4448     
4449     cls: false,
4450     align: false,
4451     bgcolor: false,
4452     charoff: false,
4453     valign: false,
4454     
4455     getAutoCreate : function(){
4456         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4457         
4458         cfg = {
4459             tag: 'tr'
4460         }
4461             
4462         if(this.cls){
4463             cfg.cls = this.cls;
4464         }
4465         if(this.align){
4466             cfg.align = this.align;
4467         }
4468         if(this.bgcolor){
4469             cfg.bgcolor = this.bgcolor;
4470         }
4471         if(this.charoff){
4472             cfg.charoff = this.charoff;
4473         }
4474         if(this.valign){
4475             cfg.valign = this.valign;
4476         }
4477         
4478         return cfg;
4479     }
4480    
4481 });
4482
4483  
4484
4485  /*
4486  * - LGPL
4487  *
4488  * table body
4489  * 
4490  */
4491
4492 /**
4493  * @class Roo.bootstrap.TableBody
4494  * @extends Roo.bootstrap.Component
4495  * Bootstrap TableBody class
4496  * @cfg {String} cls element class
4497  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4498  * @cfg {String} align Aligns the content inside the element
4499  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4500  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4501  * 
4502  * @constructor
4503  * Create a new TableBody
4504  * @param {Object} config The config object
4505  */
4506
4507 Roo.bootstrap.TableBody = function(config){
4508     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4509 };
4510
4511 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
4512     
4513     cls: false,
4514     tag: false,
4515     align: false,
4516     charoff: false,
4517     valign: false,
4518     
4519     getAutoCreate : function(){
4520         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4521         
4522         cfg = {
4523             tag: 'tbody'
4524         }
4525             
4526         if (this.cls) {
4527             cfg.cls=this.cls
4528         }
4529         if(this.tag){
4530             cfg.tag = this.tag;
4531         }
4532         
4533         if(this.align){
4534             cfg.align = this.align;
4535         }
4536         if(this.charoff){
4537             cfg.charoff = this.charoff;
4538         }
4539         if(this.valign){
4540             cfg.valign = this.valign;
4541         }
4542         
4543         return cfg;
4544     }
4545     
4546     
4547 //    initEvents : function()
4548 //    {
4549 //        
4550 //        if(!this.store){
4551 //            return;
4552 //        }
4553 //        
4554 //        this.store = Roo.factory(this.store, Roo.data);
4555 //        this.store.on('load', this.onLoad, this);
4556 //        
4557 //        this.store.load();
4558 //        
4559 //    },
4560 //    
4561 //    onLoad: function () 
4562 //    {   
4563 //        this.fireEvent('load', this);
4564 //    }
4565 //    
4566 //   
4567 });
4568
4569  
4570
4571  /*
4572  * Based on:
4573  * Ext JS Library 1.1.1
4574  * Copyright(c) 2006-2007, Ext JS, LLC.
4575  *
4576  * Originally Released Under LGPL - original licence link has changed is not relivant.
4577  *
4578  * Fork - LGPL
4579  * <script type="text/javascript">
4580  */
4581
4582 // as we use this in bootstrap.
4583 Roo.namespace('Roo.form');
4584  /**
4585  * @class Roo.form.Action
4586  * Internal Class used to handle form actions
4587  * @constructor
4588  * @param {Roo.form.BasicForm} el The form element or its id
4589  * @param {Object} config Configuration options
4590  */
4591
4592  
4593  
4594 // define the action interface
4595 Roo.form.Action = function(form, options){
4596     this.form = form;
4597     this.options = options || {};
4598 };
4599 /**
4600  * Client Validation Failed
4601  * @const 
4602  */
4603 Roo.form.Action.CLIENT_INVALID = 'client';
4604 /**
4605  * Server Validation Failed
4606  * @const 
4607  */
4608 Roo.form.Action.SERVER_INVALID = 'server';
4609  /**
4610  * Connect to Server Failed
4611  * @const 
4612  */
4613 Roo.form.Action.CONNECT_FAILURE = 'connect';
4614 /**
4615  * Reading Data from Server Failed
4616  * @const 
4617  */
4618 Roo.form.Action.LOAD_FAILURE = 'load';
4619
4620 Roo.form.Action.prototype = {
4621     type : 'default',
4622     failureType : undefined,
4623     response : undefined,
4624     result : undefined,
4625
4626     // interface method
4627     run : function(options){
4628
4629     },
4630
4631     // interface method
4632     success : function(response){
4633
4634     },
4635
4636     // interface method
4637     handleResponse : function(response){
4638
4639     },
4640
4641     // default connection failure
4642     failure : function(response){
4643         
4644         this.response = response;
4645         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4646         this.form.afterAction(this, false);
4647     },
4648
4649     processResponse : function(response){
4650         this.response = response;
4651         if(!response.responseText){
4652             return true;
4653         }
4654         this.result = this.handleResponse(response);
4655         return this.result;
4656     },
4657
4658     // utility functions used internally
4659     getUrl : function(appendParams){
4660         var url = this.options.url || this.form.url || this.form.el.dom.action;
4661         if(appendParams){
4662             var p = this.getParams();
4663             if(p){
4664                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4665             }
4666         }
4667         return url;
4668     },
4669
4670     getMethod : function(){
4671         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4672     },
4673
4674     getParams : function(){
4675         var bp = this.form.baseParams;
4676         var p = this.options.params;
4677         if(p){
4678             if(typeof p == "object"){
4679                 p = Roo.urlEncode(Roo.applyIf(p, bp));
4680             }else if(typeof p == 'string' && bp){
4681                 p += '&' + Roo.urlEncode(bp);
4682             }
4683         }else if(bp){
4684             p = Roo.urlEncode(bp);
4685         }
4686         return p;
4687     },
4688
4689     createCallback : function(){
4690         return {
4691             success: this.success,
4692             failure: this.failure,
4693             scope: this,
4694             timeout: (this.form.timeout*1000),
4695             upload: this.form.fileUpload ? this.success : undefined
4696         };
4697     }
4698 };
4699
4700 Roo.form.Action.Submit = function(form, options){
4701     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4702 };
4703
4704 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4705     type : 'submit',
4706
4707     haveProgress : false,
4708     uploadComplete : false,
4709     
4710     // uploadProgress indicator.
4711     uploadProgress : function()
4712     {
4713         if (!this.form.progressUrl) {
4714             return;
4715         }
4716         
4717         if (!this.haveProgress) {
4718             Roo.MessageBox.progress("Uploading", "Uploading");
4719         }
4720         if (this.uploadComplete) {
4721            Roo.MessageBox.hide();
4722            return;
4723         }
4724         
4725         this.haveProgress = true;
4726    
4727         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4728         
4729         var c = new Roo.data.Connection();
4730         c.request({
4731             url : this.form.progressUrl,
4732             params: {
4733                 id : uid
4734             },
4735             method: 'GET',
4736             success : function(req){
4737                //console.log(data);
4738                 var rdata = false;
4739                 var edata;
4740                 try  {
4741                    rdata = Roo.decode(req.responseText)
4742                 } catch (e) {
4743                     Roo.log("Invalid data from server..");
4744                     Roo.log(edata);
4745                     return;
4746                 }
4747                 if (!rdata || !rdata.success) {
4748                     Roo.log(rdata);
4749                     Roo.MessageBox.alert(Roo.encode(rdata));
4750                     return;
4751                 }
4752                 var data = rdata.data;
4753                 
4754                 if (this.uploadComplete) {
4755                    Roo.MessageBox.hide();
4756                    return;
4757                 }
4758                    
4759                 if (data){
4760                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4761                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4762                     );
4763                 }
4764                 this.uploadProgress.defer(2000,this);
4765             },
4766        
4767             failure: function(data) {
4768                 Roo.log('progress url failed ');
4769                 Roo.log(data);
4770             },
4771             scope : this
4772         });
4773            
4774     },
4775     
4776     
4777     run : function()
4778     {
4779         // run get Values on the form, so it syncs any secondary forms.
4780         this.form.getValues();
4781         
4782         var o = this.options;
4783         var method = this.getMethod();
4784         var isPost = method == 'POST';
4785         if(o.clientValidation === false || this.form.isValid()){
4786             
4787             if (this.form.progressUrl) {
4788                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4789                     (new Date() * 1) + '' + Math.random());
4790                     
4791             } 
4792             
4793             
4794             Roo.Ajax.request(Roo.apply(this.createCallback(), {
4795                 form:this.form.el.dom,
4796                 url:this.getUrl(!isPost),
4797                 method: method,
4798                 params:isPost ? this.getParams() : null,
4799                 isUpload: this.form.fileUpload
4800             }));
4801             
4802             this.uploadProgress();
4803
4804         }else if (o.clientValidation !== false){ // client validation failed
4805             this.failureType = Roo.form.Action.CLIENT_INVALID;
4806             this.form.afterAction(this, false);
4807         }
4808     },
4809
4810     success : function(response)
4811     {
4812         this.uploadComplete= true;
4813         if (this.haveProgress) {
4814             Roo.MessageBox.hide();
4815         }
4816         
4817         
4818         var result = this.processResponse(response);
4819         if(result === true || result.success){
4820             this.form.afterAction(this, true);
4821             return;
4822         }
4823         if(result.errors){
4824             this.form.markInvalid(result.errors);
4825             this.failureType = Roo.form.Action.SERVER_INVALID;
4826         }
4827         this.form.afterAction(this, false);
4828     },
4829     failure : function(response)
4830     {
4831         this.uploadComplete= true;
4832         if (this.haveProgress) {
4833             Roo.MessageBox.hide();
4834         }
4835         
4836         this.response = response;
4837         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4838         this.form.afterAction(this, false);
4839     },
4840     
4841     handleResponse : function(response){
4842         if(this.form.errorReader){
4843             var rs = this.form.errorReader.read(response);
4844             var errors = [];
4845             if(rs.records){
4846                 for(var i = 0, len = rs.records.length; i < len; i++) {
4847                     var r = rs.records[i];
4848                     errors[i] = r.data;
4849                 }
4850             }
4851             if(errors.length < 1){
4852                 errors = null;
4853             }
4854             return {
4855                 success : rs.success,
4856                 errors : errors
4857             };
4858         }
4859         var ret = false;
4860         try {
4861             ret = Roo.decode(response.responseText);
4862         } catch (e) {
4863             ret = {
4864                 success: false,
4865                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
4866                 errors : []
4867             };
4868         }
4869         return ret;
4870         
4871     }
4872 });
4873
4874
4875 Roo.form.Action.Load = function(form, options){
4876     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
4877     this.reader = this.form.reader;
4878 };
4879
4880 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
4881     type : 'load',
4882
4883     run : function(){
4884         
4885         Roo.Ajax.request(Roo.apply(
4886                 this.createCallback(), {
4887                     method:this.getMethod(),
4888                     url:this.getUrl(false),
4889                     params:this.getParams()
4890         }));
4891     },
4892
4893     success : function(response){
4894         
4895         var result = this.processResponse(response);
4896         if(result === true || !result.success || !result.data){
4897             this.failureType = Roo.form.Action.LOAD_FAILURE;
4898             this.form.afterAction(this, false);
4899             return;
4900         }
4901         this.form.clearInvalid();
4902         this.form.setValues(result.data);
4903         this.form.afterAction(this, true);
4904     },
4905
4906     handleResponse : function(response){
4907         if(this.form.reader){
4908             var rs = this.form.reader.read(response);
4909             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
4910             return {
4911                 success : rs.success,
4912                 data : data
4913             };
4914         }
4915         return Roo.decode(response.responseText);
4916     }
4917 });
4918
4919 Roo.form.Action.ACTION_TYPES = {
4920     'load' : Roo.form.Action.Load,
4921     'submit' : Roo.form.Action.Submit
4922 };/*
4923  * - LGPL
4924  *
4925  * form
4926  * 
4927  */
4928
4929 /**
4930  * @class Roo.bootstrap.Form
4931  * @extends Roo.bootstrap.Component
4932  * Bootstrap Form class
4933  * @cfg {String} method  GET | POST (default POST)
4934  * @cfg {String} labelAlign top | left (default top)
4935   * @cfg {String} align left  | right - for navbars
4936
4937  * 
4938  * @constructor
4939  * Create a new Form
4940  * @param {Object} config The config object
4941  */
4942
4943
4944 Roo.bootstrap.Form = function(config){
4945     Roo.bootstrap.Form.superclass.constructor.call(this, config);
4946     this.addEvents({
4947         /**
4948          * @event clientvalidation
4949          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
4950          * @param {Form} this
4951          * @param {Boolean} valid true if the form has passed client-side validation
4952          */
4953         clientvalidation: true,
4954         /**
4955          * @event beforeaction
4956          * Fires before any action is performed. Return false to cancel the action.
4957          * @param {Form} this
4958          * @param {Action} action The action to be performed
4959          */
4960         beforeaction: true,
4961         /**
4962          * @event actionfailed
4963          * Fires when an action fails.
4964          * @param {Form} this
4965          * @param {Action} action The action that failed
4966          */
4967         actionfailed : true,
4968         /**
4969          * @event actioncomplete
4970          * Fires when an action is completed.
4971          * @param {Form} this
4972          * @param {Action} action The action that completed
4973          */
4974         actioncomplete : true
4975     });
4976     
4977 };
4978
4979 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
4980       
4981      /**
4982      * @cfg {String} method
4983      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
4984      */
4985     method : 'POST',
4986     /**
4987      * @cfg {String} url
4988      * The URL to use for form actions if one isn't supplied in the action options.
4989      */
4990     /**
4991      * @cfg {Boolean} fileUpload
4992      * Set to true if this form is a file upload.
4993      */
4994      
4995     /**
4996      * @cfg {Object} baseParams
4997      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
4998      */
4999       
5000     /**
5001      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5002      */
5003     timeout: 30,
5004     /**
5005      * @cfg {Sting} align (left|right) for navbar forms
5006      */
5007     align : 'left',
5008
5009     // private
5010     activeAction : null,
5011  
5012     /**
5013      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5014      * element by passing it or its id or mask the form itself by passing in true.
5015      * @type Mixed
5016      */
5017     waitMsgTarget : false,
5018     
5019      
5020     
5021     /**
5022      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5023      * element by passing it or its id or mask the form itself by passing in true.
5024      * @type Mixed
5025      */
5026     
5027     getAutoCreate : function(){
5028         
5029         var cfg = {
5030             tag: 'form',
5031             method : this.method || 'POST',
5032             id : this.id || Roo.id(),
5033             cls : ''
5034         }
5035         if (this.parent().xtype.match(/^Nav/)) {
5036             cfg.cls = 'navbar-form navbar-' + this.align;
5037             
5038         }
5039         
5040         if (this.labelAlign == 'left' ) {
5041             cfg.cls += ' form-horizontal';
5042         }
5043         
5044         
5045         return cfg;
5046     },
5047     initEvents : function()
5048     {
5049         this.el.on('submit', this.onSubmit, this);
5050         
5051         
5052     },
5053     // private
5054     onSubmit : function(e){
5055         e.stopEvent();
5056     },
5057     
5058      /**
5059      * Returns true if client-side validation on the form is successful.
5060      * @return Boolean
5061      */
5062     isValid : function(){
5063         var items = this.getItems();
5064         var valid = true;
5065         items.each(function(f){
5066            if(!f.validate()){
5067                valid = false;
5068                
5069            }
5070         });
5071         return valid;
5072     },
5073     /**
5074      * Returns true if any fields in this form have changed since their original load.
5075      * @return Boolean
5076      */
5077     isDirty : function(){
5078         var dirty = false;
5079         var items = this.getItems();
5080         items.each(function(f){
5081            if(f.isDirty()){
5082                dirty = true;
5083                return false;
5084            }
5085            return true;
5086         });
5087         return dirty;
5088     },
5089      /**
5090      * Performs a predefined action (submit or load) or custom actions you define on this form.
5091      * @param {String} actionName The name of the action type
5092      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
5093      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
5094      * accept other config options):
5095      * <pre>
5096 Property          Type             Description
5097 ----------------  ---------------  ----------------------------------------------------------------------------------
5098 url               String           The url for the action (defaults to the form's url)
5099 method            String           The form method to use (defaults to the form's method, or POST if not defined)
5100 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
5101 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
5102                                    validate the form on the client (defaults to false)
5103      * </pre>
5104      * @return {BasicForm} this
5105      */
5106     doAction : function(action, options){
5107         if(typeof action == 'string'){
5108             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
5109         }
5110         if(this.fireEvent('beforeaction', this, action) !== false){
5111             this.beforeAction(action);
5112             action.run.defer(100, action);
5113         }
5114         return this;
5115     },
5116     
5117     // private
5118     beforeAction : function(action){
5119         var o = action.options;
5120         
5121         // not really supported yet.. ??
5122         
5123         //if(this.waitMsgTarget === true){
5124             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
5125         //}else if(this.waitMsgTarget){
5126         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
5127         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
5128         //}else {
5129         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
5130        // }
5131          
5132     },
5133
5134     // private
5135     afterAction : function(action, success){
5136         this.activeAction = null;
5137         var o = action.options;
5138         
5139         //if(this.waitMsgTarget === true){
5140             this.el.unmask();
5141         //}else if(this.waitMsgTarget){
5142         //    this.waitMsgTarget.unmask();
5143         //}else{
5144         //    Roo.MessageBox.updateProgress(1);
5145         //    Roo.MessageBox.hide();
5146        // }
5147         // 
5148         if(success){
5149             if(o.reset){
5150                 this.reset();
5151             }
5152             Roo.callback(o.success, o.scope, [this, action]);
5153             this.fireEvent('actioncomplete', this, action);
5154             
5155         }else{
5156             
5157             // failure condition..
5158             // we have a scenario where updates need confirming.
5159             // eg. if a locking scenario exists..
5160             // we look for { errors : { needs_confirm : true }} in the response.
5161             if (
5162                 (typeof(action.result) != 'undefined')  &&
5163                 (typeof(action.result.errors) != 'undefined')  &&
5164                 (typeof(action.result.errors.needs_confirm) != 'undefined')
5165            ){
5166                 var _t = this;
5167                 Roo.log("not supported yet");
5168                  /*
5169                 
5170                 Roo.MessageBox.confirm(
5171                     "Change requires confirmation",
5172                     action.result.errorMsg,
5173                     function(r) {
5174                         if (r != 'yes') {
5175                             return;
5176                         }
5177                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
5178                     }
5179                     
5180                 );
5181                 */
5182                 
5183                 
5184                 return;
5185             }
5186             
5187             Roo.callback(o.failure, o.scope, [this, action]);
5188             // show an error message if no failed handler is set..
5189             if (!this.hasListener('actionfailed')) {
5190                 Roo.log("need to add dialog support");
5191                 /*
5192                 Roo.MessageBox.alert("Error",
5193                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
5194                         action.result.errorMsg :
5195                         "Saving Failed, please check your entries or try again"
5196                 );
5197                 */
5198             }
5199             
5200             this.fireEvent('actionfailed', this, action);
5201         }
5202         
5203     },
5204     /**
5205      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
5206      * @param {String} id The value to search for
5207      * @return Field
5208      */
5209     findField : function(id){
5210         var items = this.getItems();
5211         var field = items.get(id);
5212         if(!field){
5213              items.each(function(f){
5214                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
5215                     field = f;
5216                     return false;
5217                 }
5218                 return true;
5219             });
5220         }
5221         return field || null;
5222     },
5223      /**
5224      * Mark fields in this form invalid in bulk.
5225      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
5226      * @return {BasicForm} this
5227      */
5228     markInvalid : function(errors){
5229         if(errors instanceof Array){
5230             for(var i = 0, len = errors.length; i < len; i++){
5231                 var fieldError = errors[i];
5232                 var f = this.findField(fieldError.id);
5233                 if(f){
5234                     f.markInvalid(fieldError.msg);
5235                 }
5236             }
5237         }else{
5238             var field, id;
5239             for(id in errors){
5240                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
5241                     field.markInvalid(errors[id]);
5242                 }
5243             }
5244         }
5245         //Roo.each(this.childForms || [], function (f) {
5246         //    f.markInvalid(errors);
5247         //});
5248         
5249         return this;
5250     },
5251
5252     /**
5253      * Set values for fields in this form in bulk.
5254      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
5255      * @return {BasicForm} this
5256      */
5257     setValues : function(values){
5258         if(values instanceof Array){ // array of objects
5259             for(var i = 0, len = values.length; i < len; i++){
5260                 var v = values[i];
5261                 var f = this.findField(v.id);
5262                 if(f){
5263                     f.setValue(v.value);
5264                     if(this.trackResetOnLoad){
5265                         f.originalValue = f.getValue();
5266                     }
5267                 }
5268             }
5269         }else{ // object hash
5270             var field, id;
5271             for(id in values){
5272                 if(typeof values[id] != 'function' && (field = this.findField(id))){
5273                     
5274                     if (field.setFromData && 
5275                         field.valueField && 
5276                         field.displayField &&
5277                         // combos' with local stores can 
5278                         // be queried via setValue()
5279                         // to set their value..
5280                         (field.store && !field.store.isLocal)
5281                         ) {
5282                         // it's a combo
5283                         var sd = { };
5284                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
5285                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
5286                         field.setFromData(sd);
5287                         
5288                     } else {
5289                         field.setValue(values[id]);
5290                     }
5291                     
5292                     
5293                     if(this.trackResetOnLoad){
5294                         field.originalValue = field.getValue();
5295                     }
5296                 }
5297             }
5298         }
5299          
5300         //Roo.each(this.childForms || [], function (f) {
5301         //    f.setValues(values);
5302         //});
5303                 
5304         return this;
5305     },
5306
5307     /**
5308      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
5309      * they are returned as an array.
5310      * @param {Boolean} asString
5311      * @return {Object}
5312      */
5313     getValues : function(asString){
5314         //if (this.childForms) {
5315             // copy values from the child forms
5316         //    Roo.each(this.childForms, function (f) {
5317         //        this.setValues(f.getValues());
5318         //    }, this);
5319         //}
5320         
5321         
5322         
5323         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
5324         if(asString === true){
5325             return fs;
5326         }
5327         return Roo.urlDecode(fs);
5328     },
5329     
5330     /**
5331      * Returns the fields in this form as an object with key/value pairs. 
5332      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
5333      * @return {Object}
5334      */
5335     getFieldValues : function(with_hidden)
5336     {
5337         var items = this.getItems();
5338         var ret = {};
5339         items.each(function(f){
5340             if (!f.getName()) {
5341                 return;
5342             }
5343             var v = f.getValue();
5344             if (f.inputType =='radio') {
5345                 if (typeof(ret[f.getName()]) == 'undefined') {
5346                     ret[f.getName()] = ''; // empty..
5347                 }
5348                 
5349                 if (!f.el.dom.checked) {
5350                     return;
5351                     
5352                 }
5353                 v = f.el.dom.value;
5354                 
5355             }
5356             
5357             // not sure if this supported any more..
5358             if ((typeof(v) == 'object') && f.getRawValue) {
5359                 v = f.getRawValue() ; // dates..
5360             }
5361             // combo boxes where name != hiddenName...
5362             if (f.name != f.getName()) {
5363                 ret[f.name] = f.getRawValue();
5364             }
5365             ret[f.getName()] = v;
5366         });
5367         
5368         return ret;
5369     },
5370
5371     /**
5372      * Clears all invalid messages in this form.
5373      * @return {BasicForm} this
5374      */
5375     clearInvalid : function(){
5376         var items = this.getItems();
5377         
5378         items.each(function(f){
5379            f.clearInvalid();
5380         });
5381         
5382         
5383         
5384         return this;
5385     },
5386
5387     /**
5388      * Resets this form.
5389      * @return {BasicForm} this
5390      */
5391     reset : function(){
5392         var items = this.getItems();
5393         items.each(function(f){
5394             f.reset();
5395         });
5396         
5397         Roo.each(this.childForms || [], function (f) {
5398             f.reset();
5399         });
5400        
5401         
5402         return this;
5403     },
5404     getItems : function()
5405     {
5406         var r=new Roo.util.MixedCollection(false, function(o){
5407             return o.id || (o.id = Roo.id());
5408         });
5409         var iter = function(el) {
5410             if (el.inputEl) {
5411                 r.add(el);
5412             }
5413             if (!el.items) {
5414                 return;
5415             }
5416             Roo.each(el.items,function(e) {
5417                 iter(e);
5418             });
5419             
5420             
5421         };
5422         iter(this);
5423         return r;
5424         
5425         
5426         
5427         
5428     }
5429     
5430 });
5431
5432  
5433 /*
5434  * Based on:
5435  * Ext JS Library 1.1.1
5436  * Copyright(c) 2006-2007, Ext JS, LLC.
5437  *
5438  * Originally Released Under LGPL - original licence link has changed is not relivant.
5439  *
5440  * Fork - LGPL
5441  * <script type="text/javascript">
5442  */
5443 /**
5444  * @class Roo.form.VTypes
5445  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5446  * @singleton
5447  */
5448 Roo.form.VTypes = function(){
5449     // closure these in so they are only created once.
5450     var alpha = /^[a-zA-Z_]+$/;
5451     var alphanum = /^[a-zA-Z0-9_]+$/;
5452     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5453     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5454
5455     // All these messages and functions are configurable
5456     return {
5457         /**
5458          * The function used to validate email addresses
5459          * @param {String} value The email address
5460          */
5461         'email' : function(v){
5462             return email.test(v);
5463         },
5464         /**
5465          * The error text to display when the email validation function returns false
5466          * @type String
5467          */
5468         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5469         /**
5470          * The keystroke filter mask to be applied on email input
5471          * @type RegExp
5472          */
5473         'emailMask' : /[a-z0-9_\.\-@]/i,
5474
5475         /**
5476          * The function used to validate URLs
5477          * @param {String} value The URL
5478          */
5479         'url' : function(v){
5480             return url.test(v);
5481         },
5482         /**
5483          * The error text to display when the url validation function returns false
5484          * @type String
5485          */
5486         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5487         
5488         /**
5489          * The function used to validate alpha values
5490          * @param {String} value The value
5491          */
5492         'alpha' : function(v){
5493             return alpha.test(v);
5494         },
5495         /**
5496          * The error text to display when the alpha validation function returns false
5497          * @type String
5498          */
5499         'alphaText' : 'This field should only contain letters and _',
5500         /**
5501          * The keystroke filter mask to be applied on alpha input
5502          * @type RegExp
5503          */
5504         'alphaMask' : /[a-z_]/i,
5505
5506         /**
5507          * The function used to validate alphanumeric values
5508          * @param {String} value The value
5509          */
5510         'alphanum' : function(v){
5511             return alphanum.test(v);
5512         },
5513         /**
5514          * The error text to display when the alphanumeric validation function returns false
5515          * @type String
5516          */
5517         'alphanumText' : 'This field should only contain letters, numbers and _',
5518         /**
5519          * The keystroke filter mask to be applied on alphanumeric input
5520          * @type RegExp
5521          */
5522         'alphanumMask' : /[a-z0-9_]/i
5523     };
5524 }();/*
5525  * - LGPL
5526  *
5527  * Input
5528  * 
5529  */
5530
5531 /**
5532  * @class Roo.bootstrap.Input
5533  * @extends Roo.bootstrap.Component
5534  * Bootstrap Input class
5535  * @cfg {Boolean} disabled is it disabled
5536  * @cfg {String} fieldLabel - the label associated
5537  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5538  * @cfg {String} name name of the input
5539  * @cfg {string} fieldLabel - the label associated
5540  * @cfg {string}  inputType - input / file submit ...
5541  * @cfg {string} placeholder - placeholder to put in text.
5542  * @cfg {string}  before - input group add on before
5543  * @cfg {string} after - input group add on after
5544  * @cfg {string} size - (lg|sm) or leave empty..
5545  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5546  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5547  * @cfg {Number} md colspan out of 12 for computer-sized screens
5548  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5549  * @cfg {string} value default value of the input
5550  * @cfg {Number} labelWidth set the width of label (0-12)
5551  * @cfg {String} labelAlign (top|left)
5552  * @cfg {Boolean} readOnly Specifies that the field should be read-only
5553  * 
5554  * 
5555  * @constructor
5556  * Create a new Input
5557  * @param {Object} config The config object
5558  */
5559
5560 Roo.bootstrap.Input = function(config){
5561     Roo.bootstrap.Input.superclass.constructor.call(this, config);
5562    
5563         this.addEvents({
5564             /**
5565              * @event focus
5566              * Fires when this field receives input focus.
5567              * @param {Roo.form.Field} this
5568              */
5569             focus : true,
5570             /**
5571              * @event blur
5572              * Fires when this field loses input focus.
5573              * @param {Roo.form.Field} this
5574              */
5575             blur : true,
5576             /**
5577              * @event specialkey
5578              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
5579              * {@link Roo.EventObject#getKey} to determine which key was pressed.
5580              * @param {Roo.form.Field} this
5581              * @param {Roo.EventObject} e The event object
5582              */
5583             specialkey : true,
5584             /**
5585              * @event change
5586              * Fires just before the field blurs if the field value has changed.
5587              * @param {Roo.form.Field} this
5588              * @param {Mixed} newValue The new value
5589              * @param {Mixed} oldValue The original value
5590              */
5591             change : true,
5592             /**
5593              * @event invalid
5594              * Fires after the field has been marked as invalid.
5595              * @param {Roo.form.Field} this
5596              * @param {String} msg The validation message
5597              */
5598             invalid : true,
5599             /**
5600              * @event valid
5601              * Fires after the field has been validated with no errors.
5602              * @param {Roo.form.Field} this
5603              */
5604             valid : true,
5605              /**
5606              * @event keyup
5607              * Fires after the key up
5608              * @param {Roo.form.Field} this
5609              * @param {Roo.EventObject}  e The event Object
5610              */
5611             keyup : true
5612         });
5613 };
5614
5615 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
5616      /**
5617      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5618       automatic validation (defaults to "keyup").
5619      */
5620     validationEvent : "keyup",
5621      /**
5622      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5623      */
5624     validateOnBlur : true,
5625     /**
5626      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5627      */
5628     validationDelay : 250,
5629      /**
5630      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5631      */
5632     focusClass : "x-form-focus",  // not needed???
5633     
5634        
5635     /**
5636      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5637      */
5638     invalidClass : "has-error",
5639     
5640     /**
5641      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5642      */
5643     selectOnFocus : false,
5644     
5645      /**
5646      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5647      */
5648     maskRe : null,
5649        /**
5650      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5651      */
5652     vtype : null,
5653     
5654       /**
5655      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5656      */
5657     disableKeyFilter : false,
5658     
5659        /**
5660      * @cfg {Boolean} disabled True to disable the field (defaults to false).
5661      */
5662     disabled : false,
5663      /**
5664      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5665      */
5666     allowBlank : true,
5667     /**
5668      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5669      */
5670     blankText : "This field is required",
5671     
5672      /**
5673      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5674      */
5675     minLength : 0,
5676     /**
5677      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5678      */
5679     maxLength : Number.MAX_VALUE,
5680     /**
5681      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5682      */
5683     minLengthText : "The minimum length for this field is {0}",
5684     /**
5685      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5686      */
5687     maxLengthText : "The maximum length for this field is {0}",
5688   
5689     
5690     /**
5691      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5692      * If available, this function will be called only after the basic validators all return true, and will be passed the
5693      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5694      */
5695     validator : null,
5696     /**
5697      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5698      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5699      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
5700      */
5701     regex : null,
5702     /**
5703      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5704      */
5705     regexText : "",
5706     
5707     
5708     
5709     fieldLabel : '',
5710     inputType : 'text',
5711     
5712     name : false,
5713     placeholder: false,
5714     before : false,
5715     after : false,
5716     size : false,
5717     // private
5718     hasFocus : false,
5719     preventMark: false,
5720     isFormField : true,
5721     value : '',
5722     labelWidth : 2,
5723     labelAlign : false,
5724     readOnly : false,
5725     
5726     parentLabelAlign : function()
5727     {
5728         var parent = this;
5729         while (parent.parent()) {
5730             parent = parent.parent();
5731             if (typeof(parent.labelAlign) !='undefined') {
5732                 return parent.labelAlign;
5733             }
5734         }
5735         return 'left';
5736         
5737     },
5738     
5739     getAutoCreate : function(){
5740         
5741         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5742         
5743         var id = Roo.id();
5744         
5745         var cfg = {};
5746         
5747         if(this.inputType != 'hidden'){
5748             cfg.cls = 'form-group' //input-group
5749         }
5750         
5751         var input =  {
5752             tag: 'input',
5753             id : id,
5754             type : this.inputType,
5755             value : this.value,
5756             cls : 'form-control',
5757             placeholder : this.placeholder || ''
5758             
5759         };
5760         
5761         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5762             input.maxLength = this.maxLength;
5763         }
5764         
5765         if (this.disabled) {
5766             input.disabled=true;
5767         }
5768         
5769         if (this.readOnly) {
5770             input.readonly=true;
5771         }
5772         
5773         if (this.name) {
5774             input.name = this.name;
5775         }
5776         if (this.size) {
5777             input.cls += ' input-' + this.size;
5778         }
5779         var settings=this;
5780         ['xs','sm','md','lg'].map(function(size){
5781             if (settings[size]) {
5782                 cfg.cls += ' col-' + size + '-' + settings[size];
5783             }
5784         });
5785         
5786         var inputblock = input;
5787         
5788         if (this.before || this.after) {
5789             
5790             inputblock = {
5791                 cls : 'input-group',
5792                 cn :  [] 
5793             };
5794             if (this.before && typeof(this.before) == 'string') {
5795                 
5796                 inputblock.cn.push({
5797                     tag :'span',
5798                     cls : 'roo-input-before input-group-addon',
5799                     html : this.before
5800                 });
5801             }
5802             if (this.before && typeof(this.before) == 'object') {
5803                 this.before = Roo.factory(this.before);
5804                 Roo.log(this.before);
5805                 inputblock.cn.push({
5806                     tag :'span',
5807                     cls : 'roo-input-before input-group-' +
5808                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5809                 });
5810             }
5811             
5812             inputblock.cn.push(input);
5813             
5814             if (this.after && typeof(this.after) == 'string') {
5815                 inputblock.cn.push({
5816                     tag :'span',
5817                     cls : 'roo-input-after input-group-addon',
5818                     html : this.after
5819                 });
5820             }
5821             if (this.after && typeof(this.after) == 'object') {
5822                 this.after = Roo.factory(this.after);
5823                 Roo.log(this.after);
5824                 inputblock.cn.push({
5825                     tag :'span',
5826                     cls : 'roo-input-after input-group-' +
5827                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5828                 });
5829             }
5830         };
5831         
5832         if (align ==='left' && this.fieldLabel.length) {
5833                 Roo.log("left and has label");
5834                 cfg.cn = [
5835                     
5836                     {
5837                         tag: 'label',
5838                         'for' :  id,
5839                         cls : 'control-label col-sm-' + this.labelWidth,
5840                         html : this.fieldLabel
5841                         
5842                     },
5843                     {
5844                         cls : "col-sm-" + (12 - this.labelWidth), 
5845                         cn: [
5846                             inputblock
5847                         ]
5848                     }
5849                     
5850                 ];
5851         } else if ( this.fieldLabel.length) {
5852                 Roo.log(" label");
5853                  cfg.cn = [
5854                    
5855                     {
5856                         tag: 'label',
5857                         //cls : 'input-group-addon',
5858                         html : this.fieldLabel
5859                         
5860                     },
5861                     
5862                     inputblock
5863                     
5864                 ];
5865
5866         } else {
5867             
5868                 Roo.log(" no label && no align");
5869                 cfg.cn = [
5870                     
5871                         inputblock
5872                     
5873                 ];
5874                 
5875                 
5876         };
5877         Roo.log('input-parentType: ' + this.parentType);
5878         
5879         if (this.parentType === 'Navbar' &&  this.parent().bar) {
5880            cfg.cls += ' navbar-form';
5881            Roo.log(cfg);
5882         }
5883         
5884         return cfg;
5885         
5886     },
5887     /**
5888      * return the real input element.
5889      */
5890     inputEl: function ()
5891     {
5892         return this.el.select('input.form-control',true).first();
5893     },
5894     setDisabled : function(v)
5895     {
5896         var i  = this.inputEl().dom;
5897         if (!v) {
5898             i.removeAttribute('disabled');
5899             return;
5900             
5901         }
5902         i.setAttribute('disabled','true');
5903     },
5904     initEvents : function()
5905     {
5906         
5907         this.inputEl().on("keydown" , this.fireKey,  this);
5908         this.inputEl().on("focus", this.onFocus,  this);
5909         this.inputEl().on("blur", this.onBlur,  this);
5910         
5911         this.inputEl().relayEvent('keyup', this);
5912
5913         // reference to original value for reset
5914         this.originalValue = this.getValue();
5915         //Roo.form.TextField.superclass.initEvents.call(this);
5916         if(this.validationEvent == 'keyup'){
5917             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
5918             this.inputEl().on('keyup', this.filterValidation, this);
5919         }
5920         else if(this.validationEvent !== false){
5921             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
5922         }
5923         
5924         if(this.selectOnFocus){
5925             this.on("focus", this.preFocus, this);
5926             
5927         }
5928         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
5929             this.inputEl().on("keypress", this.filterKeys, this);
5930         }
5931        /* if(this.grow){
5932             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
5933             this.el.on("click", this.autoSize,  this);
5934         }
5935         */
5936         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
5937             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
5938         }
5939         
5940         if (typeof(this.before) == 'object') {
5941             this.before.render(this.el.select('.roo-input-before',true).first());
5942         }
5943         if (typeof(this.after) == 'object') {
5944             this.after.render(this.el.select('.roo-input-after',true).first());
5945         }
5946         
5947         
5948     },
5949     filterValidation : function(e){
5950         if(!e.isNavKeyPress()){
5951             this.validationTask.delay(this.validationDelay);
5952         }
5953     },
5954      /**
5955      * Validates the field value
5956      * @return {Boolean} True if the value is valid, else false
5957      */
5958     validate : function(){
5959         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
5960         if(this.disabled || this.validateValue(this.getRawValue())){
5961             this.clearInvalid();
5962             return true;
5963         }
5964         return false;
5965     },
5966     
5967     
5968     /**
5969      * Validates a value according to the field's validation rules and marks the field as invalid
5970      * if the validation fails
5971      * @param {Mixed} value The value to validate
5972      * @return {Boolean} True if the value is valid, else false
5973      */
5974     validateValue : function(value){
5975         if(value.length < 1)  { // if it's blank
5976              if(this.allowBlank){
5977                 this.clearInvalid();
5978                 return true;
5979              }else{
5980                 this.markInvalid(this.blankText);
5981                 return false;
5982              }
5983         }
5984         if(value.length < this.minLength){
5985             this.markInvalid(String.format(this.minLengthText, this.minLength));
5986             return false;
5987         }
5988         if(value.length > this.maxLength){
5989             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
5990             return false;
5991         }
5992         if(this.vtype){
5993             var vt = Roo.form.VTypes;
5994             if(!vt[this.vtype](value, this)){
5995                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
5996                 return false;
5997             }
5998         }
5999         if(typeof this.validator == "function"){
6000             var msg = this.validator(value);
6001             if(msg !== true){
6002                 this.markInvalid(msg);
6003                 return false;
6004             }
6005         }
6006         if(this.regex && !this.regex.test(value)){
6007             this.markInvalid(this.regexText);
6008             return false;
6009         }
6010         return true;
6011     },
6012
6013     
6014     
6015      // private
6016     fireKey : function(e){
6017         //Roo.log('field ' + e.getKey());
6018         if(e.isNavKeyPress()){
6019             this.fireEvent("specialkey", this, e);
6020         }
6021     },
6022     focus : function (selectText){
6023         if(this.rendered){
6024             this.inputEl().focus();
6025             if(selectText === true){
6026                 this.inputEl().dom.select();
6027             }
6028         }
6029         return this;
6030     } ,
6031     
6032     onFocus : function(){
6033         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6034            // this.el.addClass(this.focusClass);
6035         }
6036         if(!this.hasFocus){
6037             this.hasFocus = true;
6038             this.startValue = this.getValue();
6039             this.fireEvent("focus", this);
6040         }
6041     },
6042     
6043     beforeBlur : Roo.emptyFn,
6044
6045     
6046     // private
6047     onBlur : function(){
6048         this.beforeBlur();
6049         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6050             //this.el.removeClass(this.focusClass);
6051         }
6052         this.hasFocus = false;
6053         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
6054             this.validate();
6055         }
6056         var v = this.getValue();
6057         if(String(v) !== String(this.startValue)){
6058             this.fireEvent('change', this, v, this.startValue);
6059         }
6060         this.fireEvent("blur", this);
6061     },
6062     
6063     /**
6064      * Resets the current field value to the originally loaded value and clears any validation messages
6065      */
6066     reset : function(){
6067         this.setValue(this.originalValue);
6068         this.clearInvalid();
6069     },
6070      /**
6071      * Returns the name of the field
6072      * @return {Mixed} name The name field
6073      */
6074     getName: function(){
6075         return this.name;
6076     },
6077      /**
6078      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
6079      * @return {Mixed} value The field value
6080      */
6081     getValue : function(){
6082         return this.inputEl().getValue();
6083     },
6084     /**
6085      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
6086      * @return {Mixed} value The field value
6087      */
6088     getRawValue : function(){
6089         var v = this.inputEl().getValue();
6090         
6091         return v;
6092     },
6093     
6094     /**
6095      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
6096      * @param {Mixed} value The value to set
6097      */
6098     setRawValue : function(v){
6099         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6100     },
6101     
6102     selectText : function(start, end){
6103         var v = this.getRawValue();
6104         if(v.length > 0){
6105             start = start === undefined ? 0 : start;
6106             end = end === undefined ? v.length : end;
6107             var d = this.inputEl().dom;
6108             if(d.setSelectionRange){
6109                 d.setSelectionRange(start, end);
6110             }else if(d.createTextRange){
6111                 var range = d.createTextRange();
6112                 range.moveStart("character", start);
6113                 range.moveEnd("character", v.length-end);
6114                 range.select();
6115             }
6116         }
6117     },
6118     
6119     /**
6120      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
6121      * @param {Mixed} value The value to set
6122      */
6123     setValue : function(v){
6124         this.value = v;
6125         if(this.rendered){
6126             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6127             this.validate();
6128         }
6129     },
6130     
6131     /*
6132     processValue : function(value){
6133         if(this.stripCharsRe){
6134             var newValue = value.replace(this.stripCharsRe, '');
6135             if(newValue !== value){
6136                 this.setRawValue(newValue);
6137                 return newValue;
6138             }
6139         }
6140         return value;
6141     },
6142   */
6143     preFocus : function(){
6144         
6145         if(this.selectOnFocus){
6146             this.inputEl().dom.select();
6147         }
6148     },
6149     filterKeys : function(e){
6150         var k = e.getKey();
6151         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
6152             return;
6153         }
6154         var c = e.getCharCode(), cc = String.fromCharCode(c);
6155         if(Roo.isIE && (e.isSpecialKey() || !cc)){
6156             return;
6157         }
6158         if(!this.maskRe.test(cc)){
6159             e.stopEvent();
6160         }
6161     },
6162      /**
6163      * Clear any invalid styles/messages for this field
6164      */
6165     clearInvalid : function(){
6166         
6167         if(!this.el || this.preventMark){ // not rendered
6168             return;
6169         }
6170         this.el.removeClass(this.invalidClass);
6171         /*
6172         switch(this.msgTarget){
6173             case 'qtip':
6174                 this.el.dom.qtip = '';
6175                 break;
6176             case 'title':
6177                 this.el.dom.title = '';
6178                 break;
6179             case 'under':
6180                 if(this.errorEl){
6181                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
6182                 }
6183                 break;
6184             case 'side':
6185                 if(this.errorIcon){
6186                     this.errorIcon.dom.qtip = '';
6187                     this.errorIcon.hide();
6188                     this.un('resize', this.alignErrorIcon, this);
6189                 }
6190                 break;
6191             default:
6192                 var t = Roo.getDom(this.msgTarget);
6193                 t.innerHTML = '';
6194                 t.style.display = 'none';
6195                 break;
6196         }
6197         */
6198         this.fireEvent('valid', this);
6199     },
6200      /**
6201      * Mark this field as invalid
6202      * @param {String} msg The validation message
6203      */
6204     markInvalid : function(msg){
6205         if(!this.el  || this.preventMark){ // not rendered
6206             return;
6207         }
6208         this.el.addClass(this.invalidClass);
6209         /*
6210         msg = msg || this.invalidText;
6211         switch(this.msgTarget){
6212             case 'qtip':
6213                 this.el.dom.qtip = msg;
6214                 this.el.dom.qclass = 'x-form-invalid-tip';
6215                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
6216                     Roo.QuickTips.enable();
6217                 }
6218                 break;
6219             case 'title':
6220                 this.el.dom.title = msg;
6221                 break;
6222             case 'under':
6223                 if(!this.errorEl){
6224                     var elp = this.el.findParent('.x-form-element', 5, true);
6225                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
6226                     this.errorEl.setWidth(elp.getWidth(true)-20);
6227                 }
6228                 this.errorEl.update(msg);
6229                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
6230                 break;
6231             case 'side':
6232                 if(!this.errorIcon){
6233                     var elp = this.el.findParent('.x-form-element', 5, true);
6234                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
6235                 }
6236                 this.alignErrorIcon();
6237                 this.errorIcon.dom.qtip = msg;
6238                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
6239                 this.errorIcon.show();
6240                 this.on('resize', this.alignErrorIcon, this);
6241                 break;
6242             default:
6243                 var t = Roo.getDom(this.msgTarget);
6244                 t.innerHTML = msg;
6245                 t.style.display = this.msgDisplay;
6246                 break;
6247         }
6248         */
6249         this.fireEvent('invalid', this, msg);
6250     },
6251     // private
6252     SafariOnKeyDown : function(event)
6253     {
6254         // this is a workaround for a password hang bug on chrome/ webkit.
6255         
6256         var isSelectAll = false;
6257         
6258         if(this.inputEl().dom.selectionEnd > 0){
6259             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
6260         }
6261         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
6262             event.preventDefault();
6263             this.setValue('');
6264             return;
6265         }
6266         
6267         if(isSelectAll){ // backspace and delete key
6268             
6269             event.preventDefault();
6270             // this is very hacky as keydown always get's upper case.
6271             //
6272             var cc = String.fromCharCode(event.getCharCode());
6273             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
6274             
6275         }
6276     },
6277     adjustWidth : function(tag, w){
6278         tag = tag.toLowerCase();
6279         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
6280             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
6281                 if(tag == 'input'){
6282                     return w + 2;
6283                 }
6284                 if(tag == 'textarea'){
6285                     return w-2;
6286                 }
6287             }else if(Roo.isOpera){
6288                 if(tag == 'input'){
6289                     return w + 2;
6290                 }
6291                 if(tag == 'textarea'){
6292                     return w-2;
6293                 }
6294             }
6295         }
6296         return w;
6297     }
6298     
6299 });
6300
6301  
6302 /*
6303  * - LGPL
6304  *
6305  * Input
6306  * 
6307  */
6308
6309 /**
6310  * @class Roo.bootstrap.TextArea
6311  * @extends Roo.bootstrap.Input
6312  * Bootstrap TextArea class
6313  * @cfg {Number} cols Specifies the visible width of a text area
6314  * @cfg {Number} rows Specifies the visible number of lines in a text area
6315  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
6316  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
6317  * @cfg {string} html text
6318  * 
6319  * @constructor
6320  * Create a new TextArea
6321  * @param {Object} config The config object
6322  */
6323
6324 Roo.bootstrap.TextArea = function(config){
6325     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
6326    
6327 };
6328
6329 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
6330      
6331     cols : false,
6332     rows : 5,
6333     readOnly : false,
6334     warp : 'soft',
6335     resize : false,
6336     value: false,
6337     html: false,
6338     
6339     getAutoCreate : function(){
6340         
6341         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6342         
6343         var id = Roo.id();
6344         
6345         var cfg = {};
6346         
6347         var input =  {
6348             tag: 'textarea',
6349             id : id,
6350             warp : this.warp,
6351             rows : this.rows,
6352             value : this.value || '',
6353             html: this.html || '',
6354             cls : 'form-control',
6355             placeholder : this.placeholder || '' 
6356             
6357         };
6358         
6359         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6360             input.maxLength = this.maxLength;
6361         }
6362         
6363         if(this.resize){
6364             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
6365         }
6366         
6367         if(this.cols){
6368             input.cols = this.cols;
6369         }
6370         
6371         if (this.readOnly) {
6372             input.readonly = true;
6373         }
6374         
6375         if (this.name) {
6376             input.name = this.name;
6377         }
6378         
6379         if (this.size) {
6380             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
6381         }
6382         
6383         var settings=this;
6384         ['xs','sm','md','lg'].map(function(size){
6385             if (settings[size]) {
6386                 cfg.cls += ' col-' + size + '-' + settings[size];
6387             }
6388         });
6389         
6390         var inputblock = input;
6391         
6392         if (this.before || this.after) {
6393             
6394             inputblock = {
6395                 cls : 'input-group',
6396                 cn :  [] 
6397             };
6398             if (this.before) {
6399                 inputblock.cn.push({
6400                     tag :'span',
6401                     cls : 'input-group-addon',
6402                     html : this.before
6403                 });
6404             }
6405             inputblock.cn.push(input);
6406             if (this.after) {
6407                 inputblock.cn.push({
6408                     tag :'span',
6409                     cls : 'input-group-addon',
6410                     html : this.after
6411                 });
6412             }
6413             
6414         }
6415         
6416         if (align ==='left' && this.fieldLabel.length) {
6417                 Roo.log("left and has label");
6418                 cfg.cn = [
6419                     
6420                     {
6421                         tag: 'label',
6422                         'for' :  id,
6423                         cls : 'control-label col-sm-' + this.labelWidth,
6424                         html : this.fieldLabel
6425                         
6426                     },
6427                     {
6428                         cls : "col-sm-" + (12 - this.labelWidth), 
6429                         cn: [
6430                             inputblock
6431                         ]
6432                     }
6433                     
6434                 ];
6435         } else if ( this.fieldLabel.length) {
6436                 Roo.log(" label");
6437                  cfg.cn = [
6438                    
6439                     {
6440                         tag: 'label',
6441                         //cls : 'input-group-addon',
6442                         html : this.fieldLabel
6443                         
6444                     },
6445                     
6446                     inputblock
6447                     
6448                 ];
6449
6450         } else {
6451             
6452                    Roo.log(" no label && no align");
6453                 cfg.cn = [
6454                     
6455                         inputblock
6456                     
6457                 ];
6458                 
6459                 
6460         }
6461         
6462         if (this.disabled) {
6463             input.disabled=true;
6464         }
6465         
6466         return cfg;
6467         
6468     },
6469     /**
6470      * return the real textarea element.
6471      */
6472     inputEl: function ()
6473     {
6474         return this.el.select('textarea.form-control',true).first();
6475     }
6476 });
6477
6478  
6479 /*
6480  * - LGPL
6481  *
6482  * trigger field - base class for combo..
6483  * 
6484  */
6485  
6486 /**
6487  * @class Roo.bootstrap.TriggerField
6488  * @extends Roo.bootstrap.Input
6489  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6490  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6491  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6492  * for which you can provide a custom implementation.  For example:
6493  * <pre><code>
6494 var trigger = new Roo.bootstrap.TriggerField();
6495 trigger.onTriggerClick = myTriggerFn;
6496 trigger.applyTo('my-field');
6497 </code></pre>
6498  *
6499  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6500  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6501  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
6502  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6503  * @constructor
6504  * Create a new TriggerField.
6505  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6506  * to the base TextField)
6507  */
6508 Roo.bootstrap.TriggerField = function(config){
6509     this.mimicing = false;
6510     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6511 };
6512
6513 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
6514     /**
6515      * @cfg {String} triggerClass A CSS class to apply to the trigger
6516      */
6517      /**
6518      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6519      */
6520     hideTrigger:false,
6521
6522     /** @cfg {Boolean} grow @hide */
6523     /** @cfg {Number} growMin @hide */
6524     /** @cfg {Number} growMax @hide */
6525
6526     /**
6527      * @hide 
6528      * @method
6529      */
6530     autoSize: Roo.emptyFn,
6531     // private
6532     monitorTab : true,
6533     // private
6534     deferHeight : true,
6535
6536     
6537     actionMode : 'wrap',
6538     
6539     
6540     
6541     getAutoCreate : function(){
6542        
6543         var parent = this.parent();
6544         
6545         var align = this.labelAlign || this.parentLabelAlign();
6546         
6547         var id = Roo.id();
6548         
6549         var cfg = {
6550             cls: 'form-group' //input-group
6551         };
6552         
6553         
6554         var input =  {
6555             tag: 'input',
6556             id : id,
6557             type : this.inputType,
6558             cls : 'form-control',
6559             autocomplete: 'off',
6560             placeholder : this.placeholder || '' 
6561             
6562         };
6563         if (this.name) {
6564             input.name = this.name;
6565         }
6566         if (this.size) {
6567             input.cls += ' input-' + this.size;
6568         }
6569         
6570         if (this.disabled) {
6571             input.disabled=true;
6572         }
6573         
6574         var inputblock = input;
6575         
6576         if (this.before || this.after) {
6577             
6578             inputblock = {
6579                 cls : 'input-group',
6580                 cn :  [] 
6581             };
6582             if (this.before) {
6583                 inputblock.cn.push({
6584                     tag :'span',
6585                     cls : 'input-group-addon',
6586                     html : this.before
6587                 });
6588             }
6589             inputblock.cn.push(input);
6590             if (this.after) {
6591                 inputblock.cn.push({
6592                     tag :'span',
6593                     cls : 'input-group-addon',
6594                     html : this.after
6595                 });
6596             }
6597             
6598         };
6599         
6600         var box = {
6601             tag: 'div',
6602             cn: [
6603                 {
6604                     tag: 'input',
6605                     type : 'hidden',
6606                     cls: 'form-hidden-field'
6607                 },
6608                 inputblock
6609             ]
6610             
6611         };
6612         
6613         if(this.multiple){
6614             Roo.log('multiple');
6615             
6616             box = {
6617                 tag: 'div',
6618                 cn: [
6619                     {
6620                         tag: 'input',
6621                         type : 'hidden',
6622                         cls: 'form-hidden-field'
6623                     },
6624                     {
6625                         tag: 'ul',
6626                         cls: 'select2-choices',
6627                         cn:[
6628                             {
6629                                 tag: 'li',
6630                                 cls: 'select2-search-field',
6631                                 cn: [
6632
6633                                     inputblock
6634                                 ]
6635                             }
6636                         ]
6637                     }
6638                 ]
6639             }
6640         };
6641         
6642         var combobox = {
6643             cls: 'select2-container input-group',
6644             cn: [
6645                 box,
6646                 {
6647                     tag: 'ul',
6648                     cls: 'typeahead typeahead-long dropdown-menu',
6649                     style: 'display:none'
6650                 }
6651             ]
6652         };
6653         
6654         if(!this.multiple){
6655             combobox.cn.push({
6656                 tag :'span',
6657                 cls : 'input-group-addon btn dropdown-toggle',
6658                 cn : [
6659                     {
6660                         tag: 'span',
6661                         cls: 'caret'
6662                     },
6663                     {
6664                         tag: 'span',
6665                         cls: 'combobox-clear',
6666                         cn  : [
6667                             {
6668                                 tag : 'i',
6669                                 cls: 'icon-remove'
6670                             }
6671                         ]
6672                     }
6673                 ]
6674
6675             })
6676         }
6677         
6678         if(this.multiple){
6679             combobox.cls += ' select2-container-multi';
6680         }
6681         
6682         if (align ==='left' && this.fieldLabel.length) {
6683             
6684                 Roo.log("left and has label");
6685                 cfg.cn = [
6686                     
6687                     {
6688                         tag: 'label',
6689                         'for' :  id,
6690                         cls : 'control-label col-sm-' + this.labelWidth,
6691                         html : this.fieldLabel
6692                         
6693                     },
6694                     {
6695                         cls : "col-sm-" + (12 - this.labelWidth), 
6696                         cn: [
6697                             combobox
6698                         ]
6699                     }
6700                     
6701                 ];
6702         } else if ( this.fieldLabel.length) {
6703                 Roo.log(" label");
6704                  cfg.cn = [
6705                    
6706                     {
6707                         tag: 'label',
6708                         //cls : 'input-group-addon',
6709                         html : this.fieldLabel
6710                         
6711                     },
6712                     
6713                     combobox
6714                     
6715                 ];
6716
6717         } else {
6718             
6719                 Roo.log(" no label && no align");
6720                 cfg = combobox
6721                      
6722                 
6723         }
6724          
6725         var settings=this;
6726         ['xs','sm','md','lg'].map(function(size){
6727             if (settings[size]) {
6728                 cfg.cls += ' col-' + size + '-' + settings[size];
6729             }
6730         });
6731         
6732         return cfg;
6733         
6734     },
6735     
6736     
6737     
6738     // private
6739     onResize : function(w, h){
6740 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6741 //        if(typeof w == 'number'){
6742 //            var x = w - this.trigger.getWidth();
6743 //            this.inputEl().setWidth(this.adjustWidth('input', x));
6744 //            this.trigger.setStyle('left', x+'px');
6745 //        }
6746     },
6747
6748     // private
6749     adjustSize : Roo.BoxComponent.prototype.adjustSize,
6750
6751     // private
6752     getResizeEl : function(){
6753         return this.inputEl();
6754     },
6755
6756     // private
6757     getPositionEl : function(){
6758         return this.inputEl();
6759     },
6760
6761     // private
6762     alignErrorIcon : function(){
6763         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6764     },
6765
6766     // private
6767     initEvents : function(){
6768         
6769         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6770         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6771         if(!this.multiple){
6772             this.trigger = this.el.select('span.dropdown-toggle',true).first();
6773             if(this.hideTrigger){
6774                 this.trigger.setDisplayed(false);
6775             }
6776             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6777         }
6778         
6779         if(this.multiple){
6780             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6781         }
6782         
6783         //this.trigger.addClassOnOver('x-form-trigger-over');
6784         //this.trigger.addClassOnClick('x-form-trigger-click');
6785         
6786         //if(!this.width){
6787         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6788         //}
6789     },
6790
6791     // private
6792     initTrigger : function(){
6793        
6794     },
6795
6796     // private
6797     onDestroy : function(){
6798         if(this.trigger){
6799             this.trigger.removeAllListeners();
6800           //  this.trigger.remove();
6801         }
6802         //if(this.wrap){
6803         //    this.wrap.remove();
6804         //}
6805         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6806     },
6807
6808     // private
6809     onFocus : function(){
6810         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6811         /*
6812         if(!this.mimicing){
6813             this.wrap.addClass('x-trigger-wrap-focus');
6814             this.mimicing = true;
6815             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6816             if(this.monitorTab){
6817                 this.el.on("keydown", this.checkTab, this);
6818             }
6819         }
6820         */
6821     },
6822
6823     // private
6824     checkTab : function(e){
6825         if(e.getKey() == e.TAB){
6826             this.triggerBlur();
6827         }
6828     },
6829
6830     // private
6831     onBlur : function(){
6832         // do nothing
6833     },
6834
6835     // private
6836     mimicBlur : function(e, t){
6837         /*
6838         if(!this.wrap.contains(t) && this.validateBlur()){
6839             this.triggerBlur();
6840         }
6841         */
6842     },
6843
6844     // private
6845     triggerBlur : function(){
6846         this.mimicing = false;
6847         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
6848         if(this.monitorTab){
6849             this.el.un("keydown", this.checkTab, this);
6850         }
6851         //this.wrap.removeClass('x-trigger-wrap-focus');
6852         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
6853     },
6854
6855     // private
6856     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
6857     validateBlur : function(e, t){
6858         return true;
6859     },
6860
6861     // private
6862     onDisable : function(){
6863         this.inputEl().dom.disabled = true;
6864         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
6865         //if(this.wrap){
6866         //    this.wrap.addClass('x-item-disabled');
6867         //}
6868     },
6869
6870     // private
6871     onEnable : function(){
6872         this.inputEl().dom.disabled = false;
6873         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
6874         //if(this.wrap){
6875         //    this.el.removeClass('x-item-disabled');
6876         //}
6877     },
6878
6879     // private
6880     onShow : function(){
6881         var ae = this.getActionEl();
6882         
6883         if(ae){
6884             ae.dom.style.display = '';
6885             ae.dom.style.visibility = 'visible';
6886         }
6887     },
6888
6889     // private
6890     
6891     onHide : function(){
6892         var ae = this.getActionEl();
6893         ae.dom.style.display = 'none';
6894     },
6895
6896     /**
6897      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
6898      * by an implementing function.
6899      * @method
6900      * @param {EventObject} e
6901      */
6902     onTriggerClick : Roo.emptyFn
6903 });
6904  /*
6905  * Based on:
6906  * Ext JS Library 1.1.1
6907  * Copyright(c) 2006-2007, Ext JS, LLC.
6908  *
6909  * Originally Released Under LGPL - original licence link has changed is not relivant.
6910  *
6911  * Fork - LGPL
6912  * <script type="text/javascript">
6913  */
6914
6915
6916 /**
6917  * @class Roo.data.SortTypes
6918  * @singleton
6919  * Defines the default sorting (casting?) comparison functions used when sorting data.
6920  */
6921 Roo.data.SortTypes = {
6922     /**
6923      * Default sort that does nothing
6924      * @param {Mixed} s The value being converted
6925      * @return {Mixed} The comparison value
6926      */
6927     none : function(s){
6928         return s;
6929     },
6930     
6931     /**
6932      * The regular expression used to strip tags
6933      * @type {RegExp}
6934      * @property
6935      */
6936     stripTagsRE : /<\/?[^>]+>/gi,
6937     
6938     /**
6939      * Strips all HTML tags to sort on text only
6940      * @param {Mixed} s The value being converted
6941      * @return {String} The comparison value
6942      */
6943     asText : function(s){
6944         return String(s).replace(this.stripTagsRE, "");
6945     },
6946     
6947     /**
6948      * Strips all HTML tags to sort on text only - Case insensitive
6949      * @param {Mixed} s The value being converted
6950      * @return {String} The comparison value
6951      */
6952     asUCText : function(s){
6953         return String(s).toUpperCase().replace(this.stripTagsRE, "");
6954     },
6955     
6956     /**
6957      * Case insensitive string
6958      * @param {Mixed} s The value being converted
6959      * @return {String} The comparison value
6960      */
6961     asUCString : function(s) {
6962         return String(s).toUpperCase();
6963     },
6964     
6965     /**
6966      * Date sorting
6967      * @param {Mixed} s The value being converted
6968      * @return {Number} The comparison value
6969      */
6970     asDate : function(s) {
6971         if(!s){
6972             return 0;
6973         }
6974         if(s instanceof Date){
6975             return s.getTime();
6976         }
6977         return Date.parse(String(s));
6978     },
6979     
6980     /**
6981      * Float sorting
6982      * @param {Mixed} s The value being converted
6983      * @return {Float} The comparison value
6984      */
6985     asFloat : function(s) {
6986         var val = parseFloat(String(s).replace(/,/g, ""));
6987         if(isNaN(val)) val = 0;
6988         return val;
6989     },
6990     
6991     /**
6992      * Integer sorting
6993      * @param {Mixed} s The value being converted
6994      * @return {Number} The comparison value
6995      */
6996     asInt : function(s) {
6997         var val = parseInt(String(s).replace(/,/g, ""));
6998         if(isNaN(val)) val = 0;
6999         return val;
7000     }
7001 };/*
7002  * Based on:
7003  * Ext JS Library 1.1.1
7004  * Copyright(c) 2006-2007, Ext JS, LLC.
7005  *
7006  * Originally Released Under LGPL - original licence link has changed is not relivant.
7007  *
7008  * Fork - LGPL
7009  * <script type="text/javascript">
7010  */
7011
7012 /**
7013 * @class Roo.data.Record
7014  * Instances of this class encapsulate both record <em>definition</em> information, and record
7015  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
7016  * to access Records cached in an {@link Roo.data.Store} object.<br>
7017  * <p>
7018  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
7019  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
7020  * objects.<br>
7021  * <p>
7022  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
7023  * @constructor
7024  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
7025  * {@link #create}. The parameters are the same.
7026  * @param {Array} data An associative Array of data values keyed by the field name.
7027  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
7028  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
7029  * not specified an integer id is generated.
7030  */
7031 Roo.data.Record = function(data, id){
7032     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
7033     this.data = data;
7034 };
7035
7036 /**
7037  * Generate a constructor for a specific record layout.
7038  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
7039  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
7040  * Each field definition object may contain the following properties: <ul>
7041  * <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,
7042  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
7043  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
7044  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
7045  * is being used, then this is a string containing the javascript expression to reference the data relative to 
7046  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
7047  * to the data item relative to the record element. If the mapping expression is the same as the field name,
7048  * this may be omitted.</p></li>
7049  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
7050  * <ul><li>auto (Default, implies no conversion)</li>
7051  * <li>string</li>
7052  * <li>int</li>
7053  * <li>float</li>
7054  * <li>boolean</li>
7055  * <li>date</li></ul></p></li>
7056  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
7057  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
7058  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
7059  * by the Reader into an object that will be stored in the Record. It is passed the
7060  * following parameters:<ul>
7061  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
7062  * </ul></p></li>
7063  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
7064  * </ul>
7065  * <br>usage:<br><pre><code>
7066 var TopicRecord = Roo.data.Record.create(
7067     {name: 'title', mapping: 'topic_title'},
7068     {name: 'author', mapping: 'username'},
7069     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
7070     {name: 'lastPost', mapping: 'post_time', type: 'date'},
7071     {name: 'lastPoster', mapping: 'user2'},
7072     {name: 'excerpt', mapping: 'post_text'}
7073 );
7074
7075 var myNewRecord = new TopicRecord({
7076     title: 'Do my job please',
7077     author: 'noobie',
7078     totalPosts: 1,
7079     lastPost: new Date(),
7080     lastPoster: 'Animal',
7081     excerpt: 'No way dude!'
7082 });
7083 myStore.add(myNewRecord);
7084 </code></pre>
7085  * @method create
7086  * @static
7087  */
7088 Roo.data.Record.create = function(o){
7089     var f = function(){
7090         f.superclass.constructor.apply(this, arguments);
7091     };
7092     Roo.extend(f, Roo.data.Record);
7093     var p = f.prototype;
7094     p.fields = new Roo.util.MixedCollection(false, function(field){
7095         return field.name;
7096     });
7097     for(var i = 0, len = o.length; i < len; i++){
7098         p.fields.add(new Roo.data.Field(o[i]));
7099     }
7100     f.getField = function(name){
7101         return p.fields.get(name);  
7102     };
7103     return f;
7104 };
7105
7106 Roo.data.Record.AUTO_ID = 1000;
7107 Roo.data.Record.EDIT = 'edit';
7108 Roo.data.Record.REJECT = 'reject';
7109 Roo.data.Record.COMMIT = 'commit';
7110
7111 Roo.data.Record.prototype = {
7112     /**
7113      * Readonly flag - true if this record has been modified.
7114      * @type Boolean
7115      */
7116     dirty : false,
7117     editing : false,
7118     error: null,
7119     modified: null,
7120
7121     // private
7122     join : function(store){
7123         this.store = store;
7124     },
7125
7126     /**
7127      * Set the named field to the specified value.
7128      * @param {String} name The name of the field to set.
7129      * @param {Object} value The value to set the field to.
7130      */
7131     set : function(name, value){
7132         if(this.data[name] == value){
7133             return;
7134         }
7135         this.dirty = true;
7136         if(!this.modified){
7137             this.modified = {};
7138         }
7139         if(typeof this.modified[name] == 'undefined'){
7140             this.modified[name] = this.data[name];
7141         }
7142         this.data[name] = value;
7143         if(!this.editing && this.store){
7144             this.store.afterEdit(this);
7145         }       
7146     },
7147
7148     /**
7149      * Get the value of the named field.
7150      * @param {String} name The name of the field to get the value of.
7151      * @return {Object} The value of the field.
7152      */
7153     get : function(name){
7154         return this.data[name]; 
7155     },
7156
7157     // private
7158     beginEdit : function(){
7159         this.editing = true;
7160         this.modified = {}; 
7161     },
7162
7163     // private
7164     cancelEdit : function(){
7165         this.editing = false;
7166         delete this.modified;
7167     },
7168
7169     // private
7170     endEdit : function(){
7171         this.editing = false;
7172         if(this.dirty && this.store){
7173             this.store.afterEdit(this);
7174         }
7175     },
7176
7177     /**
7178      * Usually called by the {@link Roo.data.Store} which owns the Record.
7179      * Rejects all changes made to the Record since either creation, or the last commit operation.
7180      * Modified fields are reverted to their original values.
7181      * <p>
7182      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7183      * of reject operations.
7184      */
7185     reject : function(){
7186         var m = this.modified;
7187         for(var n in m){
7188             if(typeof m[n] != "function"){
7189                 this.data[n] = m[n];
7190             }
7191         }
7192         this.dirty = false;
7193         delete this.modified;
7194         this.editing = false;
7195         if(this.store){
7196             this.store.afterReject(this);
7197         }
7198     },
7199
7200     /**
7201      * Usually called by the {@link Roo.data.Store} which owns the Record.
7202      * Commits all changes made to the Record since either creation, or the last commit operation.
7203      * <p>
7204      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7205      * of commit operations.
7206      */
7207     commit : function(){
7208         this.dirty = false;
7209         delete this.modified;
7210         this.editing = false;
7211         if(this.store){
7212             this.store.afterCommit(this);
7213         }
7214     },
7215
7216     // private
7217     hasError : function(){
7218         return this.error != null;
7219     },
7220
7221     // private
7222     clearError : function(){
7223         this.error = null;
7224     },
7225
7226     /**
7227      * Creates a copy of this record.
7228      * @param {String} id (optional) A new record id if you don't want to use this record's id
7229      * @return {Record}
7230      */
7231     copy : function(newId) {
7232         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
7233     }
7234 };/*
7235  * Based on:
7236  * Ext JS Library 1.1.1
7237  * Copyright(c) 2006-2007, Ext JS, LLC.
7238  *
7239  * Originally Released Under LGPL - original licence link has changed is not relivant.
7240  *
7241  * Fork - LGPL
7242  * <script type="text/javascript">
7243  */
7244
7245
7246
7247 /**
7248  * @class Roo.data.Store
7249  * @extends Roo.util.Observable
7250  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
7251  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
7252  * <p>
7253  * 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
7254  * has no knowledge of the format of the data returned by the Proxy.<br>
7255  * <p>
7256  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
7257  * instances from the data object. These records are cached and made available through accessor functions.
7258  * @constructor
7259  * Creates a new Store.
7260  * @param {Object} config A config object containing the objects needed for the Store to access data,
7261  * and read the data into Records.
7262  */
7263 Roo.data.Store = function(config){
7264     this.data = new Roo.util.MixedCollection(false);
7265     this.data.getKey = function(o){
7266         return o.id;
7267     };
7268     this.baseParams = {};
7269     // private
7270     this.paramNames = {
7271         "start" : "start",
7272         "limit" : "limit",
7273         "sort" : "sort",
7274         "dir" : "dir",
7275         "multisort" : "_multisort"
7276     };
7277
7278     if(config && config.data){
7279         this.inlineData = config.data;
7280         delete config.data;
7281     }
7282
7283     Roo.apply(this, config);
7284     
7285     if(this.reader){ // reader passed
7286         this.reader = Roo.factory(this.reader, Roo.data);
7287         this.reader.xmodule = this.xmodule || false;
7288         if(!this.recordType){
7289             this.recordType = this.reader.recordType;
7290         }
7291         if(this.reader.onMetaChange){
7292             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
7293         }
7294     }
7295
7296     if(this.recordType){
7297         this.fields = this.recordType.prototype.fields;
7298     }
7299     this.modified = [];
7300
7301     this.addEvents({
7302         /**
7303          * @event datachanged
7304          * Fires when the data cache has changed, and a widget which is using this Store
7305          * as a Record cache should refresh its view.
7306          * @param {Store} this
7307          */
7308         datachanged : true,
7309         /**
7310          * @event metachange
7311          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
7312          * @param {Store} this
7313          * @param {Object} meta The JSON metadata
7314          */
7315         metachange : true,
7316         /**
7317          * @event add
7318          * Fires when Records have been added to the Store
7319          * @param {Store} this
7320          * @param {Roo.data.Record[]} records The array of Records added
7321          * @param {Number} index The index at which the record(s) were added
7322          */
7323         add : true,
7324         /**
7325          * @event remove
7326          * Fires when a Record has been removed from the Store
7327          * @param {Store} this
7328          * @param {Roo.data.Record} record The Record that was removed
7329          * @param {Number} index The index at which the record was removed
7330          */
7331         remove : true,
7332         /**
7333          * @event update
7334          * Fires when a Record has been updated
7335          * @param {Store} this
7336          * @param {Roo.data.Record} record The Record that was updated
7337          * @param {String} operation The update operation being performed.  Value may be one of:
7338          * <pre><code>
7339  Roo.data.Record.EDIT
7340  Roo.data.Record.REJECT
7341  Roo.data.Record.COMMIT
7342          * </code></pre>
7343          */
7344         update : true,
7345         /**
7346          * @event clear
7347          * Fires when the data cache has been cleared.
7348          * @param {Store} this
7349          */
7350         clear : true,
7351         /**
7352          * @event beforeload
7353          * Fires before a request is made for a new data object.  If the beforeload handler returns false
7354          * the load action will be canceled.
7355          * @param {Store} this
7356          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7357          */
7358         beforeload : true,
7359         /**
7360          * @event beforeloadadd
7361          * Fires after a new set of Records has been loaded.
7362          * @param {Store} this
7363          * @param {Roo.data.Record[]} records The Records that were loaded
7364          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7365          */
7366         beforeloadadd : true,
7367         /**
7368          * @event load
7369          * Fires after a new set of Records has been loaded, before they are added to the store.
7370          * @param {Store} this
7371          * @param {Roo.data.Record[]} records The Records that were loaded
7372          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7373          * @params {Object} return from reader
7374          */
7375         load : true,
7376         /**
7377          * @event loadexception
7378          * Fires if an exception occurs in the Proxy during loading.
7379          * Called with the signature of the Proxy's "loadexception" event.
7380          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
7381          * 
7382          * @param {Proxy} 
7383          * @param {Object} return from JsonData.reader() - success, totalRecords, records
7384          * @param {Object} load options 
7385          * @param {Object} jsonData from your request (normally this contains the Exception)
7386          */
7387         loadexception : true
7388     });
7389     
7390     if(this.proxy){
7391         this.proxy = Roo.factory(this.proxy, Roo.data);
7392         this.proxy.xmodule = this.xmodule || false;
7393         this.relayEvents(this.proxy,  ["loadexception"]);
7394     }
7395     this.sortToggle = {};
7396     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
7397
7398     Roo.data.Store.superclass.constructor.call(this);
7399
7400     if(this.inlineData){
7401         this.loadData(this.inlineData);
7402         delete this.inlineData;
7403     }
7404 };
7405
7406 Roo.extend(Roo.data.Store, Roo.util.Observable, {
7407      /**
7408     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
7409     * without a remote query - used by combo/forms at present.
7410     */
7411     
7412     /**
7413     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
7414     */
7415     /**
7416     * @cfg {Array} data Inline data to be loaded when the store is initialized.
7417     */
7418     /**
7419     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
7420     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
7421     */
7422     /**
7423     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
7424     * on any HTTP request
7425     */
7426     /**
7427     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7428     */
7429     /**
7430     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7431     */
7432     multiSort: false,
7433     /**
7434     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7435     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7436     */
7437     remoteSort : false,
7438
7439     /**
7440     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7441      * loaded or when a record is removed. (defaults to false).
7442     */
7443     pruneModifiedRecords : false,
7444
7445     // private
7446     lastOptions : null,
7447
7448     /**
7449      * Add Records to the Store and fires the add event.
7450      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7451      */
7452     add : function(records){
7453         records = [].concat(records);
7454         for(var i = 0, len = records.length; i < len; i++){
7455             records[i].join(this);
7456         }
7457         var index = this.data.length;
7458         this.data.addAll(records);
7459         this.fireEvent("add", this, records, index);
7460     },
7461
7462     /**
7463      * Remove a Record from the Store and fires the remove event.
7464      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7465      */
7466     remove : function(record){
7467         var index = this.data.indexOf(record);
7468         this.data.removeAt(index);
7469         if(this.pruneModifiedRecords){
7470             this.modified.remove(record);
7471         }
7472         this.fireEvent("remove", this, record, index);
7473     },
7474
7475     /**
7476      * Remove all Records from the Store and fires the clear event.
7477      */
7478     removeAll : function(){
7479         this.data.clear();
7480         if(this.pruneModifiedRecords){
7481             this.modified = [];
7482         }
7483         this.fireEvent("clear", this);
7484     },
7485
7486     /**
7487      * Inserts Records to the Store at the given index and fires the add event.
7488      * @param {Number} index The start index at which to insert the passed Records.
7489      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7490      */
7491     insert : function(index, records){
7492         records = [].concat(records);
7493         for(var i = 0, len = records.length; i < len; i++){
7494             this.data.insert(index, records[i]);
7495             records[i].join(this);
7496         }
7497         this.fireEvent("add", this, records, index);
7498     },
7499
7500     /**
7501      * Get the index within the cache of the passed Record.
7502      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7503      * @return {Number} The index of the passed Record. Returns -1 if not found.
7504      */
7505     indexOf : function(record){
7506         return this.data.indexOf(record);
7507     },
7508
7509     /**
7510      * Get the index within the cache of the Record with the passed id.
7511      * @param {String} id The id of the Record to find.
7512      * @return {Number} The index of the Record. Returns -1 if not found.
7513      */
7514     indexOfId : function(id){
7515         return this.data.indexOfKey(id);
7516     },
7517
7518     /**
7519      * Get the Record with the specified id.
7520      * @param {String} id The id of the Record to find.
7521      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7522      */
7523     getById : function(id){
7524         return this.data.key(id);
7525     },
7526
7527     /**
7528      * Get the Record at the specified index.
7529      * @param {Number} index The index of the Record to find.
7530      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7531      */
7532     getAt : function(index){
7533         return this.data.itemAt(index);
7534     },
7535
7536     /**
7537      * Returns a range of Records between specified indices.
7538      * @param {Number} startIndex (optional) The starting index (defaults to 0)
7539      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7540      * @return {Roo.data.Record[]} An array of Records
7541      */
7542     getRange : function(start, end){
7543         return this.data.getRange(start, end);
7544     },
7545
7546     // private
7547     storeOptions : function(o){
7548         o = Roo.apply({}, o);
7549         delete o.callback;
7550         delete o.scope;
7551         this.lastOptions = o;
7552     },
7553
7554     /**
7555      * Loads the Record cache from the configured Proxy using the configured Reader.
7556      * <p>
7557      * If using remote paging, then the first load call must specify the <em>start</em>
7558      * and <em>limit</em> properties in the options.params property to establish the initial
7559      * position within the dataset, and the number of Records to cache on each read from the Proxy.
7560      * <p>
7561      * <strong>It is important to note that for remote data sources, loading is asynchronous,
7562      * and this call will return before the new data has been loaded. Perform any post-processing
7563      * in a callback function, or in a "load" event handler.</strong>
7564      * <p>
7565      * @param {Object} options An object containing properties which control loading options:<ul>
7566      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7567      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7568      * passed the following arguments:<ul>
7569      * <li>r : Roo.data.Record[]</li>
7570      * <li>options: Options object from the load call</li>
7571      * <li>success: Boolean success indicator</li></ul></li>
7572      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7573      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7574      * </ul>
7575      */
7576     load : function(options){
7577         options = options || {};
7578         if(this.fireEvent("beforeload", this, options) !== false){
7579             this.storeOptions(options);
7580             var p = Roo.apply(options.params || {}, this.baseParams);
7581             // if meta was not loaded from remote source.. try requesting it.
7582             if (!this.reader.metaFromRemote) {
7583                 p._requestMeta = 1;
7584             }
7585             if(this.sortInfo && this.remoteSort){
7586                 var pn = this.paramNames;
7587                 p[pn["sort"]] = this.sortInfo.field;
7588                 p[pn["dir"]] = this.sortInfo.direction;
7589             }
7590             if (this.multiSort) {
7591                 var pn = this.paramNames;
7592                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7593             }
7594             
7595             this.proxy.load(p, this.reader, this.loadRecords, this, options);
7596         }
7597     },
7598
7599     /**
7600      * Reloads the Record cache from the configured Proxy using the configured Reader and
7601      * the options from the last load operation performed.
7602      * @param {Object} options (optional) An object containing properties which may override the options
7603      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7604      * the most recently used options are reused).
7605      */
7606     reload : function(options){
7607         this.load(Roo.applyIf(options||{}, this.lastOptions));
7608     },
7609
7610     // private
7611     // Called as a callback by the Reader during a load operation.
7612     loadRecords : function(o, options, success){
7613         if(!o || success === false){
7614             if(success !== false){
7615                 this.fireEvent("load", this, [], options, o);
7616             }
7617             if(options.callback){
7618                 options.callback.call(options.scope || this, [], options, false);
7619             }
7620             return;
7621         }
7622         // if data returned failure - throw an exception.
7623         if (o.success === false) {
7624             // show a message if no listener is registered.
7625             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7626                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7627             }
7628             // loadmask wil be hooked into this..
7629             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7630             return;
7631         }
7632         var r = o.records, t = o.totalRecords || r.length;
7633         
7634         this.fireEvent("beforeloadadd", this, r, options, o);
7635         
7636         if(!options || options.add !== true){
7637             if(this.pruneModifiedRecords){
7638                 this.modified = [];
7639             }
7640             for(var i = 0, len = r.length; i < len; i++){
7641                 r[i].join(this);
7642             }
7643             if(this.snapshot){
7644                 this.data = this.snapshot;
7645                 delete this.snapshot;
7646             }
7647             this.data.clear();
7648             this.data.addAll(r);
7649             this.totalLength = t;
7650             this.applySort();
7651             this.fireEvent("datachanged", this);
7652         }else{
7653             this.totalLength = Math.max(t, this.data.length+r.length);
7654             this.add(r);
7655         }
7656         this.fireEvent("load", this, r, options, o);
7657         if(options.callback){
7658             options.callback.call(options.scope || this, r, options, true);
7659         }
7660     },
7661
7662
7663     /**
7664      * Loads data from a passed data block. A Reader which understands the format of the data
7665      * must have been configured in the constructor.
7666      * @param {Object} data The data block from which to read the Records.  The format of the data expected
7667      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7668      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7669      */
7670     loadData : function(o, append){
7671         var r = this.reader.readRecords(o);
7672         this.loadRecords(r, {add: append}, true);
7673     },
7674
7675     /**
7676      * Gets the number of cached records.
7677      * <p>
7678      * <em>If using paging, this may not be the total size of the dataset. If the data object
7679      * used by the Reader contains the dataset size, then the getTotalCount() function returns
7680      * the data set size</em>
7681      */
7682     getCount : function(){
7683         return this.data.length || 0;
7684     },
7685
7686     /**
7687      * Gets the total number of records in the dataset as returned by the server.
7688      * <p>
7689      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7690      * the dataset size</em>
7691      */
7692     getTotalCount : function(){
7693         return this.totalLength || 0;
7694     },
7695
7696     /**
7697      * Returns the sort state of the Store as an object with two properties:
7698      * <pre><code>
7699  field {String} The name of the field by which the Records are sorted
7700  direction {String} The sort order, "ASC" or "DESC"
7701      * </code></pre>
7702      */
7703     getSortState : function(){
7704         return this.sortInfo;
7705     },
7706
7707     // private
7708     applySort : function(){
7709         if(this.sortInfo && !this.remoteSort){
7710             var s = this.sortInfo, f = s.field;
7711             var st = this.fields.get(f).sortType;
7712             var fn = function(r1, r2){
7713                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7714                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7715             };
7716             this.data.sort(s.direction, fn);
7717             if(this.snapshot && this.snapshot != this.data){
7718                 this.snapshot.sort(s.direction, fn);
7719             }
7720         }
7721     },
7722
7723     /**
7724      * Sets the default sort column and order to be used by the next load operation.
7725      * @param {String} fieldName The name of the field to sort by.
7726      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7727      */
7728     setDefaultSort : function(field, dir){
7729         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7730     },
7731
7732     /**
7733      * Sort the Records.
7734      * If remote sorting is used, the sort is performed on the server, and the cache is
7735      * reloaded. If local sorting is used, the cache is sorted internally.
7736      * @param {String} fieldName The name of the field to sort by.
7737      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7738      */
7739     sort : function(fieldName, dir){
7740         var f = this.fields.get(fieldName);
7741         if(!dir){
7742             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7743             
7744             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7745                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7746             }else{
7747                 dir = f.sortDir;
7748             }
7749         }
7750         this.sortToggle[f.name] = dir;
7751         this.sortInfo = {field: f.name, direction: dir};
7752         if(!this.remoteSort){
7753             this.applySort();
7754             this.fireEvent("datachanged", this);
7755         }else{
7756             this.load(this.lastOptions);
7757         }
7758     },
7759
7760     /**
7761      * Calls the specified function for each of the Records in the cache.
7762      * @param {Function} fn The function to call. The Record is passed as the first parameter.
7763      * Returning <em>false</em> aborts and exits the iteration.
7764      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7765      */
7766     each : function(fn, scope){
7767         this.data.each(fn, scope);
7768     },
7769
7770     /**
7771      * Gets all records modified since the last commit.  Modified records are persisted across load operations
7772      * (e.g., during paging).
7773      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7774      */
7775     getModifiedRecords : function(){
7776         return this.modified;
7777     },
7778
7779     // private
7780     createFilterFn : function(property, value, anyMatch){
7781         if(!value.exec){ // not a regex
7782             value = String(value);
7783             if(value.length == 0){
7784                 return false;
7785             }
7786             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7787         }
7788         return function(r){
7789             return value.test(r.data[property]);
7790         };
7791     },
7792
7793     /**
7794      * Sums the value of <i>property</i> for each record between start and end and returns the result.
7795      * @param {String} property A field on your records
7796      * @param {Number} start The record index to start at (defaults to 0)
7797      * @param {Number} end The last record index to include (defaults to length - 1)
7798      * @return {Number} The sum
7799      */
7800     sum : function(property, start, end){
7801         var rs = this.data.items, v = 0;
7802         start = start || 0;
7803         end = (end || end === 0) ? end : rs.length-1;
7804
7805         for(var i = start; i <= end; i++){
7806             v += (rs[i].data[property] || 0);
7807         }
7808         return v;
7809     },
7810
7811     /**
7812      * Filter the records by a specified property.
7813      * @param {String} field A field on your records
7814      * @param {String/RegExp} value Either a string that the field
7815      * should start with or a RegExp to test against the field
7816      * @param {Boolean} anyMatch True to match any part not just the beginning
7817      */
7818     filter : function(property, value, anyMatch){
7819         var fn = this.createFilterFn(property, value, anyMatch);
7820         return fn ? this.filterBy(fn) : this.clearFilter();
7821     },
7822
7823     /**
7824      * Filter by a function. The specified function will be called with each
7825      * record in this data source. If the function returns true the record is included,
7826      * otherwise it is filtered.
7827      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7828      * @param {Object} scope (optional) The scope of the function (defaults to this)
7829      */
7830     filterBy : function(fn, scope){
7831         this.snapshot = this.snapshot || this.data;
7832         this.data = this.queryBy(fn, scope||this);
7833         this.fireEvent("datachanged", this);
7834     },
7835
7836     /**
7837      * Query the records by a specified property.
7838      * @param {String} field A field on your records
7839      * @param {String/RegExp} value Either a string that the field
7840      * should start with or a RegExp to test against the field
7841      * @param {Boolean} anyMatch True to match any part not just the beginning
7842      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7843      */
7844     query : function(property, value, anyMatch){
7845         var fn = this.createFilterFn(property, value, anyMatch);
7846         return fn ? this.queryBy(fn) : this.data.clone();
7847     },
7848
7849     /**
7850      * Query by a function. The specified function will be called with each
7851      * record in this data source. If the function returns true the record is included
7852      * in the results.
7853      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7854      * @param {Object} scope (optional) The scope of the function (defaults to this)
7855       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7856      **/
7857     queryBy : function(fn, scope){
7858         var data = this.snapshot || this.data;
7859         return data.filterBy(fn, scope||this);
7860     },
7861
7862     /**
7863      * Collects unique values for a particular dataIndex from this store.
7864      * @param {String} dataIndex The property to collect
7865      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
7866      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
7867      * @return {Array} An array of the unique values
7868      **/
7869     collect : function(dataIndex, allowNull, bypassFilter){
7870         var d = (bypassFilter === true && this.snapshot) ?
7871                 this.snapshot.items : this.data.items;
7872         var v, sv, r = [], l = {};
7873         for(var i = 0, len = d.length; i < len; i++){
7874             v = d[i].data[dataIndex];
7875             sv = String(v);
7876             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
7877                 l[sv] = true;
7878                 r[r.length] = v;
7879             }
7880         }
7881         return r;
7882     },
7883
7884     /**
7885      * Revert to a view of the Record cache with no filtering applied.
7886      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
7887      */
7888     clearFilter : function(suppressEvent){
7889         if(this.snapshot && this.snapshot != this.data){
7890             this.data = this.snapshot;
7891             delete this.snapshot;
7892             if(suppressEvent !== true){
7893                 this.fireEvent("datachanged", this);
7894             }
7895         }
7896     },
7897
7898     // private
7899     afterEdit : function(record){
7900         if(this.modified.indexOf(record) == -1){
7901             this.modified.push(record);
7902         }
7903         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
7904     },
7905     
7906     // private
7907     afterReject : function(record){
7908         this.modified.remove(record);
7909         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
7910     },
7911
7912     // private
7913     afterCommit : function(record){
7914         this.modified.remove(record);
7915         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
7916     },
7917
7918     /**
7919      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
7920      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
7921      */
7922     commitChanges : function(){
7923         var m = this.modified.slice(0);
7924         this.modified = [];
7925         for(var i = 0, len = m.length; i < len; i++){
7926             m[i].commit();
7927         }
7928     },
7929
7930     /**
7931      * Cancel outstanding changes on all changed records.
7932      */
7933     rejectChanges : function(){
7934         var m = this.modified.slice(0);
7935         this.modified = [];
7936         for(var i = 0, len = m.length; i < len; i++){
7937             m[i].reject();
7938         }
7939     },
7940
7941     onMetaChange : function(meta, rtype, o){
7942         this.recordType = rtype;
7943         this.fields = rtype.prototype.fields;
7944         delete this.snapshot;
7945         this.sortInfo = meta.sortInfo || this.sortInfo;
7946         this.modified = [];
7947         this.fireEvent('metachange', this, this.reader.meta);
7948     },
7949     
7950     moveIndex : function(data, type)
7951     {
7952         var index = this.indexOf(data);
7953         
7954         var newIndex = index + type;
7955         
7956         this.remove(data);
7957         
7958         this.insert(newIndex, data);
7959         
7960     }
7961 });/*
7962  * Based on:
7963  * Ext JS Library 1.1.1
7964  * Copyright(c) 2006-2007, Ext JS, LLC.
7965  *
7966  * Originally Released Under LGPL - original licence link has changed is not relivant.
7967  *
7968  * Fork - LGPL
7969  * <script type="text/javascript">
7970  */
7971
7972 /**
7973  * @class Roo.data.SimpleStore
7974  * @extends Roo.data.Store
7975  * Small helper class to make creating Stores from Array data easier.
7976  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
7977  * @cfg {Array} fields An array of field definition objects, or field name strings.
7978  * @cfg {Array} data The multi-dimensional array of data
7979  * @constructor
7980  * @param {Object} config
7981  */
7982 Roo.data.SimpleStore = function(config){
7983     Roo.data.SimpleStore.superclass.constructor.call(this, {
7984         isLocal : true,
7985         reader: new Roo.data.ArrayReader({
7986                 id: config.id
7987             },
7988             Roo.data.Record.create(config.fields)
7989         ),
7990         proxy : new Roo.data.MemoryProxy(config.data)
7991     });
7992     this.load();
7993 };
7994 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
7995  * Based on:
7996  * Ext JS Library 1.1.1
7997  * Copyright(c) 2006-2007, Ext JS, LLC.
7998  *
7999  * Originally Released Under LGPL - original licence link has changed is not relivant.
8000  *
8001  * Fork - LGPL
8002  * <script type="text/javascript">
8003  */
8004
8005 /**
8006 /**
8007  * @extends Roo.data.Store
8008  * @class Roo.data.JsonStore
8009  * Small helper class to make creating Stores for JSON data easier. <br/>
8010 <pre><code>
8011 var store = new Roo.data.JsonStore({
8012     url: 'get-images.php',
8013     root: 'images',
8014     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
8015 });
8016 </code></pre>
8017  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
8018  * JsonReader and HttpProxy (unless inline data is provided).</b>
8019  * @cfg {Array} fields An array of field definition objects, or field name strings.
8020  * @constructor
8021  * @param {Object} config
8022  */
8023 Roo.data.JsonStore = function(c){
8024     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
8025         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
8026         reader: new Roo.data.JsonReader(c, c.fields)
8027     }));
8028 };
8029 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
8030  * Based on:
8031  * Ext JS Library 1.1.1
8032  * Copyright(c) 2006-2007, Ext JS, LLC.
8033  *
8034  * Originally Released Under LGPL - original licence link has changed is not relivant.
8035  *
8036  * Fork - LGPL
8037  * <script type="text/javascript">
8038  */
8039
8040  
8041 Roo.data.Field = function(config){
8042     if(typeof config == "string"){
8043         config = {name: config};
8044     }
8045     Roo.apply(this, config);
8046     
8047     if(!this.type){
8048         this.type = "auto";
8049     }
8050     
8051     var st = Roo.data.SortTypes;
8052     // named sortTypes are supported, here we look them up
8053     if(typeof this.sortType == "string"){
8054         this.sortType = st[this.sortType];
8055     }
8056     
8057     // set default sortType for strings and dates
8058     if(!this.sortType){
8059         switch(this.type){
8060             case "string":
8061                 this.sortType = st.asUCString;
8062                 break;
8063             case "date":
8064                 this.sortType = st.asDate;
8065                 break;
8066             default:
8067                 this.sortType = st.none;
8068         }
8069     }
8070
8071     // define once
8072     var stripRe = /[\$,%]/g;
8073
8074     // prebuilt conversion function for this field, instead of
8075     // switching every time we're reading a value
8076     if(!this.convert){
8077         var cv, dateFormat = this.dateFormat;
8078         switch(this.type){
8079             case "":
8080             case "auto":
8081             case undefined:
8082                 cv = function(v){ return v; };
8083                 break;
8084             case "string":
8085                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
8086                 break;
8087             case "int":
8088                 cv = function(v){
8089                     return v !== undefined && v !== null && v !== '' ?
8090                            parseInt(String(v).replace(stripRe, ""), 10) : '';
8091                     };
8092                 break;
8093             case "float":
8094                 cv = function(v){
8095                     return v !== undefined && v !== null && v !== '' ?
8096                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
8097                     };
8098                 break;
8099             case "bool":
8100             case "boolean":
8101                 cv = function(v){ return v === true || v === "true" || v == 1; };
8102                 break;
8103             case "date":
8104                 cv = function(v){
8105                     if(!v){
8106                         return '';
8107                     }
8108                     if(v instanceof Date){
8109                         return v;
8110                     }
8111                     if(dateFormat){
8112                         if(dateFormat == "timestamp"){
8113                             return new Date(v*1000);
8114                         }
8115                         return Date.parseDate(v, dateFormat);
8116                     }
8117                     var parsed = Date.parse(v);
8118                     return parsed ? new Date(parsed) : null;
8119                 };
8120              break;
8121             
8122         }
8123         this.convert = cv;
8124     }
8125 };
8126
8127 Roo.data.Field.prototype = {
8128     dateFormat: null,
8129     defaultValue: "",
8130     mapping: null,
8131     sortType : null,
8132     sortDir : "ASC"
8133 };/*
8134  * Based on:
8135  * Ext JS Library 1.1.1
8136  * Copyright(c) 2006-2007, Ext JS, LLC.
8137  *
8138  * Originally Released Under LGPL - original licence link has changed is not relivant.
8139  *
8140  * Fork - LGPL
8141  * <script type="text/javascript">
8142  */
8143  
8144 // Base class for reading structured data from a data source.  This class is intended to be
8145 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
8146
8147 /**
8148  * @class Roo.data.DataReader
8149  * Base class for reading structured data from a data source.  This class is intended to be
8150  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
8151  */
8152
8153 Roo.data.DataReader = function(meta, recordType){
8154     
8155     this.meta = meta;
8156     
8157     this.recordType = recordType instanceof Array ? 
8158         Roo.data.Record.create(recordType) : recordType;
8159 };
8160
8161 Roo.data.DataReader.prototype = {
8162      /**
8163      * Create an empty record
8164      * @param {Object} data (optional) - overlay some values
8165      * @return {Roo.data.Record} record created.
8166      */
8167     newRow :  function(d) {
8168         var da =  {};
8169         this.recordType.prototype.fields.each(function(c) {
8170             switch( c.type) {
8171                 case 'int' : da[c.name] = 0; break;
8172                 case 'date' : da[c.name] = new Date(); break;
8173                 case 'float' : da[c.name] = 0.0; break;
8174                 case 'boolean' : da[c.name] = false; break;
8175                 default : da[c.name] = ""; break;
8176             }
8177             
8178         });
8179         return new this.recordType(Roo.apply(da, d));
8180     }
8181     
8182 };/*
8183  * Based on:
8184  * Ext JS Library 1.1.1
8185  * Copyright(c) 2006-2007, Ext JS, LLC.
8186  *
8187  * Originally Released Under LGPL - original licence link has changed is not relivant.
8188  *
8189  * Fork - LGPL
8190  * <script type="text/javascript">
8191  */
8192
8193 /**
8194  * @class Roo.data.DataProxy
8195  * @extends Roo.data.Observable
8196  * This class is an abstract base class for implementations which provide retrieval of
8197  * unformatted data objects.<br>
8198  * <p>
8199  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
8200  * (of the appropriate type which knows how to parse the data object) to provide a block of
8201  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
8202  * <p>
8203  * Custom implementations must implement the load method as described in
8204  * {@link Roo.data.HttpProxy#load}.
8205  */
8206 Roo.data.DataProxy = function(){
8207     this.addEvents({
8208         /**
8209          * @event beforeload
8210          * Fires before a network request is made to retrieve a data object.
8211          * @param {Object} This DataProxy object.
8212          * @param {Object} params The params parameter to the load function.
8213          */
8214         beforeload : true,
8215         /**
8216          * @event load
8217          * Fires before the load method's callback is called.
8218          * @param {Object} This DataProxy object.
8219          * @param {Object} o The data object.
8220          * @param {Object} arg The callback argument object passed to the load function.
8221          */
8222         load : true,
8223         /**
8224          * @event loadexception
8225          * Fires if an Exception occurs during data retrieval.
8226          * @param {Object} This DataProxy object.
8227          * @param {Object} o The data object.
8228          * @param {Object} arg The callback argument object passed to the load function.
8229          * @param {Object} e The Exception.
8230          */
8231         loadexception : true
8232     });
8233     Roo.data.DataProxy.superclass.constructor.call(this);
8234 };
8235
8236 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
8237
8238     /**
8239      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
8240      */
8241 /*
8242  * Based on:
8243  * Ext JS Library 1.1.1
8244  * Copyright(c) 2006-2007, Ext JS, LLC.
8245  *
8246  * Originally Released Under LGPL - original licence link has changed is not relivant.
8247  *
8248  * Fork - LGPL
8249  * <script type="text/javascript">
8250  */
8251 /**
8252  * @class Roo.data.MemoryProxy
8253  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
8254  * to the Reader when its load method is called.
8255  * @constructor
8256  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
8257  */
8258 Roo.data.MemoryProxy = function(data){
8259     if (data.data) {
8260         data = data.data;
8261     }
8262     Roo.data.MemoryProxy.superclass.constructor.call(this);
8263     this.data = data;
8264 };
8265
8266 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
8267     /**
8268      * Load data from the requested source (in this case an in-memory
8269      * data object passed to the constructor), read the data object into
8270      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8271      * process that block using the passed callback.
8272      * @param {Object} params This parameter is not used by the MemoryProxy class.
8273      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8274      * object into a block of Roo.data.Records.
8275      * @param {Function} callback The function into which to pass the block of Roo.data.records.
8276      * The function must be passed <ul>
8277      * <li>The Record block object</li>
8278      * <li>The "arg" argument from the load function</li>
8279      * <li>A boolean success indicator</li>
8280      * </ul>
8281      * @param {Object} scope The scope in which to call the callback
8282      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8283      */
8284     load : function(params, reader, callback, scope, arg){
8285         params = params || {};
8286         var result;
8287         try {
8288             result = reader.readRecords(this.data);
8289         }catch(e){
8290             this.fireEvent("loadexception", this, arg, null, e);
8291             callback.call(scope, null, arg, false);
8292             return;
8293         }
8294         callback.call(scope, result, arg, true);
8295     },
8296     
8297     // private
8298     update : function(params, records){
8299         
8300     }
8301 });/*
8302  * Based on:
8303  * Ext JS Library 1.1.1
8304  * Copyright(c) 2006-2007, Ext JS, LLC.
8305  *
8306  * Originally Released Under LGPL - original licence link has changed is not relivant.
8307  *
8308  * Fork - LGPL
8309  * <script type="text/javascript">
8310  */
8311 /**
8312  * @class Roo.data.HttpProxy
8313  * @extends Roo.data.DataProxy
8314  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
8315  * configured to reference a certain URL.<br><br>
8316  * <p>
8317  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
8318  * from which the running page was served.<br><br>
8319  * <p>
8320  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
8321  * <p>
8322  * Be aware that to enable the browser to parse an XML document, the server must set
8323  * the Content-Type header in the HTTP response to "text/xml".
8324  * @constructor
8325  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
8326  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
8327  * will be used to make the request.
8328  */
8329 Roo.data.HttpProxy = function(conn){
8330     Roo.data.HttpProxy.superclass.constructor.call(this);
8331     // is conn a conn config or a real conn?
8332     this.conn = conn;
8333     this.useAjax = !conn || !conn.events;
8334   
8335 };
8336
8337 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
8338     // thse are take from connection...
8339     
8340     /**
8341      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
8342      */
8343     /**
8344      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
8345      * extra parameters to each request made by this object. (defaults to undefined)
8346      */
8347     /**
8348      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
8349      *  to each request made by this object. (defaults to undefined)
8350      */
8351     /**
8352      * @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)
8353      */
8354     /**
8355      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
8356      */
8357      /**
8358      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
8359      * @type Boolean
8360      */
8361   
8362
8363     /**
8364      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
8365      * @type Boolean
8366      */
8367     /**
8368      * Return the {@link Roo.data.Connection} object being used by this Proxy.
8369      * @return {Connection} The Connection object. This object may be used to subscribe to events on
8370      * a finer-grained basis than the DataProxy events.
8371      */
8372     getConnection : function(){
8373         return this.useAjax ? Roo.Ajax : this.conn;
8374     },
8375
8376     /**
8377      * Load data from the configured {@link Roo.data.Connection}, read the data object into
8378      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
8379      * process that block using the passed callback.
8380      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8381      * for the request to the remote server.
8382      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8383      * object into a block of Roo.data.Records.
8384      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8385      * The function must be passed <ul>
8386      * <li>The Record block object</li>
8387      * <li>The "arg" argument from the load function</li>
8388      * <li>A boolean success indicator</li>
8389      * </ul>
8390      * @param {Object} scope The scope in which to call the callback
8391      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8392      */
8393     load : function(params, reader, callback, scope, arg){
8394         if(this.fireEvent("beforeload", this, params) !== false){
8395             var  o = {
8396                 params : params || {},
8397                 request: {
8398                     callback : callback,
8399                     scope : scope,
8400                     arg : arg
8401                 },
8402                 reader: reader,
8403                 callback : this.loadResponse,
8404                 scope: this
8405             };
8406             if(this.useAjax){
8407                 Roo.applyIf(o, this.conn);
8408                 if(this.activeRequest){
8409                     Roo.Ajax.abort(this.activeRequest);
8410                 }
8411                 this.activeRequest = Roo.Ajax.request(o);
8412             }else{
8413                 this.conn.request(o);
8414             }
8415         }else{
8416             callback.call(scope||this, null, arg, false);
8417         }
8418     },
8419
8420     // private
8421     loadResponse : function(o, success, response){
8422         delete this.activeRequest;
8423         if(!success){
8424             this.fireEvent("loadexception", this, o, response);
8425             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8426             return;
8427         }
8428         var result;
8429         try {
8430             result = o.reader.read(response);
8431         }catch(e){
8432             this.fireEvent("loadexception", this, o, response, e);
8433             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8434             return;
8435         }
8436         
8437         this.fireEvent("load", this, o, o.request.arg);
8438         o.request.callback.call(o.request.scope, result, o.request.arg, true);
8439     },
8440
8441     // private
8442     update : function(dataSet){
8443
8444     },
8445
8446     // private
8447     updateResponse : function(dataSet){
8448
8449     }
8450 });/*
8451  * Based on:
8452  * Ext JS Library 1.1.1
8453  * Copyright(c) 2006-2007, Ext JS, LLC.
8454  *
8455  * Originally Released Under LGPL - original licence link has changed is not relivant.
8456  *
8457  * Fork - LGPL
8458  * <script type="text/javascript">
8459  */
8460
8461 /**
8462  * @class Roo.data.ScriptTagProxy
8463  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8464  * other than the originating domain of the running page.<br><br>
8465  * <p>
8466  * <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
8467  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8468  * <p>
8469  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8470  * source code that is used as the source inside a &lt;script> tag.<br><br>
8471  * <p>
8472  * In order for the browser to process the returned data, the server must wrap the data object
8473  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8474  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8475  * depending on whether the callback name was passed:
8476  * <p>
8477  * <pre><code>
8478 boolean scriptTag = false;
8479 String cb = request.getParameter("callback");
8480 if (cb != null) {
8481     scriptTag = true;
8482     response.setContentType("text/javascript");
8483 } else {
8484     response.setContentType("application/x-json");
8485 }
8486 Writer out = response.getWriter();
8487 if (scriptTag) {
8488     out.write(cb + "(");
8489 }
8490 out.print(dataBlock.toJsonString());
8491 if (scriptTag) {
8492     out.write(");");
8493 }
8494 </pre></code>
8495  *
8496  * @constructor
8497  * @param {Object} config A configuration object.
8498  */
8499 Roo.data.ScriptTagProxy = function(config){
8500     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8501     Roo.apply(this, config);
8502     this.head = document.getElementsByTagName("head")[0];
8503 };
8504
8505 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8506
8507 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8508     /**
8509      * @cfg {String} url The URL from which to request the data object.
8510      */
8511     /**
8512      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8513      */
8514     timeout : 30000,
8515     /**
8516      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8517      * the server the name of the callback function set up by the load call to process the returned data object.
8518      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8519      * javascript output which calls this named function passing the data object as its only parameter.
8520      */
8521     callbackParam : "callback",
8522     /**
8523      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8524      * name to the request.
8525      */
8526     nocache : true,
8527
8528     /**
8529      * Load data from the configured URL, read the data object into
8530      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8531      * process that block using the passed callback.
8532      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8533      * for the request to the remote server.
8534      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8535      * object into a block of Roo.data.Records.
8536      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8537      * The function must be passed <ul>
8538      * <li>The Record block object</li>
8539      * <li>The "arg" argument from the load function</li>
8540      * <li>A boolean success indicator</li>
8541      * </ul>
8542      * @param {Object} scope The scope in which to call the callback
8543      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8544      */
8545     load : function(params, reader, callback, scope, arg){
8546         if(this.fireEvent("beforeload", this, params) !== false){
8547
8548             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8549
8550             var url = this.url;
8551             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8552             if(this.nocache){
8553                 url += "&_dc=" + (new Date().getTime());
8554             }
8555             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8556             var trans = {
8557                 id : transId,
8558                 cb : "stcCallback"+transId,
8559                 scriptId : "stcScript"+transId,
8560                 params : params,
8561                 arg : arg,
8562                 url : url,
8563                 callback : callback,
8564                 scope : scope,
8565                 reader : reader
8566             };
8567             var conn = this;
8568
8569             window[trans.cb] = function(o){
8570                 conn.handleResponse(o, trans);
8571             };
8572
8573             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8574
8575             if(this.autoAbort !== false){
8576                 this.abort();
8577             }
8578
8579             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8580
8581             var script = document.createElement("script");
8582             script.setAttribute("src", url);
8583             script.setAttribute("type", "text/javascript");
8584             script.setAttribute("id", trans.scriptId);
8585             this.head.appendChild(script);
8586
8587             this.trans = trans;
8588         }else{
8589             callback.call(scope||this, null, arg, false);
8590         }
8591     },
8592
8593     // private
8594     isLoading : function(){
8595         return this.trans ? true : false;
8596     },
8597
8598     /**
8599      * Abort the current server request.
8600      */
8601     abort : function(){
8602         if(this.isLoading()){
8603             this.destroyTrans(this.trans);
8604         }
8605     },
8606
8607     // private
8608     destroyTrans : function(trans, isLoaded){
8609         this.head.removeChild(document.getElementById(trans.scriptId));
8610         clearTimeout(trans.timeoutId);
8611         if(isLoaded){
8612             window[trans.cb] = undefined;
8613             try{
8614                 delete window[trans.cb];
8615             }catch(e){}
8616         }else{
8617             // if hasn't been loaded, wait for load to remove it to prevent script error
8618             window[trans.cb] = function(){
8619                 window[trans.cb] = undefined;
8620                 try{
8621                     delete window[trans.cb];
8622                 }catch(e){}
8623             };
8624         }
8625     },
8626
8627     // private
8628     handleResponse : function(o, trans){
8629         this.trans = false;
8630         this.destroyTrans(trans, true);
8631         var result;
8632         try {
8633             result = trans.reader.readRecords(o);
8634         }catch(e){
8635             this.fireEvent("loadexception", this, o, trans.arg, e);
8636             trans.callback.call(trans.scope||window, null, trans.arg, false);
8637             return;
8638         }
8639         this.fireEvent("load", this, o, trans.arg);
8640         trans.callback.call(trans.scope||window, result, trans.arg, true);
8641     },
8642
8643     // private
8644     handleFailure : function(trans){
8645         this.trans = false;
8646         this.destroyTrans(trans, false);
8647         this.fireEvent("loadexception", this, null, trans.arg);
8648         trans.callback.call(trans.scope||window, null, trans.arg, false);
8649     }
8650 });/*
8651  * Based on:
8652  * Ext JS Library 1.1.1
8653  * Copyright(c) 2006-2007, Ext JS, LLC.
8654  *
8655  * Originally Released Under LGPL - original licence link has changed is not relivant.
8656  *
8657  * Fork - LGPL
8658  * <script type="text/javascript">
8659  */
8660
8661 /**
8662  * @class Roo.data.JsonReader
8663  * @extends Roo.data.DataReader
8664  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8665  * based on mappings in a provided Roo.data.Record constructor.
8666  * 
8667  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8668  * in the reply previously. 
8669  * 
8670  * <p>
8671  * Example code:
8672  * <pre><code>
8673 var RecordDef = Roo.data.Record.create([
8674     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
8675     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
8676 ]);
8677 var myReader = new Roo.data.JsonReader({
8678     totalProperty: "results",    // The property which contains the total dataset size (optional)
8679     root: "rows",                // The property which contains an Array of row objects
8680     id: "id"                     // The property within each row object that provides an ID for the record (optional)
8681 }, RecordDef);
8682 </code></pre>
8683  * <p>
8684  * This would consume a JSON file like this:
8685  * <pre><code>
8686 { 'results': 2, 'rows': [
8687     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8688     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8689 }
8690 </code></pre>
8691  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8692  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8693  * paged from the remote server.
8694  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8695  * @cfg {String} root name of the property which contains the Array of row objects.
8696  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8697  * @constructor
8698  * Create a new JsonReader
8699  * @param {Object} meta Metadata configuration options
8700  * @param {Object} recordType Either an Array of field definition objects,
8701  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8702  */
8703 Roo.data.JsonReader = function(meta, recordType){
8704     
8705     meta = meta || {};
8706     // set some defaults:
8707     Roo.applyIf(meta, {
8708         totalProperty: 'total',
8709         successProperty : 'success',
8710         root : 'data',
8711         id : 'id'
8712     });
8713     
8714     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8715 };
8716 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8717     
8718     /**
8719      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
8720      * Used by Store query builder to append _requestMeta to params.
8721      * 
8722      */
8723     metaFromRemote : false,
8724     /**
8725      * This method is only used by a DataProxy which has retrieved data from a remote server.
8726      * @param {Object} response The XHR object which contains the JSON data in its responseText.
8727      * @return {Object} data A data block which is used by an Roo.data.Store object as
8728      * a cache of Roo.data.Records.
8729      */
8730     read : function(response){
8731         var json = response.responseText;
8732        
8733         var o = /* eval:var:o */ eval("("+json+")");
8734         if(!o) {
8735             throw {message: "JsonReader.read: Json object not found"};
8736         }
8737         
8738         if(o.metaData){
8739             
8740             delete this.ef;
8741             this.metaFromRemote = true;
8742             this.meta = o.metaData;
8743             this.recordType = Roo.data.Record.create(o.metaData.fields);
8744             this.onMetaChange(this.meta, this.recordType, o);
8745         }
8746         return this.readRecords(o);
8747     },
8748
8749     // private function a store will implement
8750     onMetaChange : function(meta, recordType, o){
8751
8752     },
8753
8754     /**
8755          * @ignore
8756          */
8757     simpleAccess: function(obj, subsc) {
8758         return obj[subsc];
8759     },
8760
8761         /**
8762          * @ignore
8763          */
8764     getJsonAccessor: function(){
8765         var re = /[\[\.]/;
8766         return function(expr) {
8767             try {
8768                 return(re.test(expr))
8769                     ? new Function("obj", "return obj." + expr)
8770                     : function(obj){
8771                         return obj[expr];
8772                     };
8773             } catch(e){}
8774             return Roo.emptyFn;
8775         };
8776     }(),
8777
8778     /**
8779      * Create a data block containing Roo.data.Records from an XML document.
8780      * @param {Object} o An object which contains an Array of row objects in the property specified
8781      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8782      * which contains the total size of the dataset.
8783      * @return {Object} data A data block which is used by an Roo.data.Store object as
8784      * a cache of Roo.data.Records.
8785      */
8786     readRecords : function(o){
8787         /**
8788          * After any data loads, the raw JSON data is available for further custom processing.
8789          * @type Object
8790          */
8791         this.o = o;
8792         var s = this.meta, Record = this.recordType,
8793             f = Record.prototype.fields, fi = f.items, fl = f.length;
8794
8795 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
8796         if (!this.ef) {
8797             if(s.totalProperty) {
8798                     this.getTotal = this.getJsonAccessor(s.totalProperty);
8799                 }
8800                 if(s.successProperty) {
8801                     this.getSuccess = this.getJsonAccessor(s.successProperty);
8802                 }
8803                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8804                 if (s.id) {
8805                         var g = this.getJsonAccessor(s.id);
8806                         this.getId = function(rec) {
8807                                 var r = g(rec);
8808                                 return (r === undefined || r === "") ? null : r;
8809                         };
8810                 } else {
8811                         this.getId = function(){return null;};
8812                 }
8813             this.ef = [];
8814             for(var jj = 0; jj < fl; jj++){
8815                 f = fi[jj];
8816                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8817                 this.ef[jj] = this.getJsonAccessor(map);
8818             }
8819         }
8820
8821         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8822         if(s.totalProperty){
8823             var vt = parseInt(this.getTotal(o), 10);
8824             if(!isNaN(vt)){
8825                 totalRecords = vt;
8826             }
8827         }
8828         if(s.successProperty){
8829             var vs = this.getSuccess(o);
8830             if(vs === false || vs === 'false'){
8831                 success = false;
8832             }
8833         }
8834         var records = [];
8835             for(var i = 0; i < c; i++){
8836                     var n = root[i];
8837                 var values = {};
8838                 var id = this.getId(n);
8839                 for(var j = 0; j < fl; j++){
8840                     f = fi[j];
8841                 var v = this.ef[j](n);
8842                 if (!f.convert) {
8843                     Roo.log('missing convert for ' + f.name);
8844                     Roo.log(f);
8845                     continue;
8846                 }
8847                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
8848                 }
8849                 var record = new Record(values, id);
8850                 record.json = n;
8851                 records[i] = record;
8852             }
8853             return {
8854             raw : o,
8855                 success : success,
8856                 records : records,
8857                 totalRecords : totalRecords
8858             };
8859     }
8860 });/*
8861  * Based on:
8862  * Ext JS Library 1.1.1
8863  * Copyright(c) 2006-2007, Ext JS, LLC.
8864  *
8865  * Originally Released Under LGPL - original licence link has changed is not relivant.
8866  *
8867  * Fork - LGPL
8868  * <script type="text/javascript">
8869  */
8870
8871 /**
8872  * @class Roo.data.ArrayReader
8873  * @extends Roo.data.DataReader
8874  * Data reader class to create an Array of Roo.data.Record objects from an Array.
8875  * Each element of that Array represents a row of data fields. The
8876  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
8877  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
8878  * <p>
8879  * Example code:.
8880  * <pre><code>
8881 var RecordDef = Roo.data.Record.create([
8882     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
8883     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
8884 ]);
8885 var myReader = new Roo.data.ArrayReader({
8886     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
8887 }, RecordDef);
8888 </code></pre>
8889  * <p>
8890  * This would consume an Array like this:
8891  * <pre><code>
8892 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
8893   </code></pre>
8894  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
8895  * @constructor
8896  * Create a new JsonReader
8897  * @param {Object} meta Metadata configuration options.
8898  * @param {Object} recordType Either an Array of field definition objects
8899  * as specified to {@link Roo.data.Record#create},
8900  * or an {@link Roo.data.Record} object
8901  * created using {@link Roo.data.Record#create}.
8902  */
8903 Roo.data.ArrayReader = function(meta, recordType){
8904     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
8905 };
8906
8907 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
8908     /**
8909      * Create a data block containing Roo.data.Records from an XML document.
8910      * @param {Object} o An Array of row objects which represents the dataset.
8911      * @return {Object} data A data block which is used by an Roo.data.Store object as
8912      * a cache of Roo.data.Records.
8913      */
8914     readRecords : function(o){
8915         var sid = this.meta ? this.meta.id : null;
8916         var recordType = this.recordType, fields = recordType.prototype.fields;
8917         var records = [];
8918         var root = o;
8919             for(var i = 0; i < root.length; i++){
8920                     var n = root[i];
8921                 var values = {};
8922                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
8923                 for(var j = 0, jlen = fields.length; j < jlen; j++){
8924                 var f = fields.items[j];
8925                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
8926                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
8927                 v = f.convert(v);
8928                 values[f.name] = v;
8929             }
8930                 var record = new recordType(values, id);
8931                 record.json = n;
8932                 records[records.length] = record;
8933             }
8934             return {
8935                 records : records,
8936                 totalRecords : records.length
8937             };
8938     }
8939 });/*
8940  * - LGPL
8941  * * 
8942  */
8943
8944 /**
8945  * @class Roo.bootstrap.ComboBox
8946  * @extends Roo.bootstrap.TriggerField
8947  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
8948  * @cfg {Boolean} append (true|false) default false
8949  * @constructor
8950  * Create a new ComboBox.
8951  * @param {Object} config Configuration options
8952  */
8953 Roo.bootstrap.ComboBox = function(config){
8954     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
8955     this.addEvents({
8956         /**
8957          * @event expand
8958          * Fires when the dropdown list is expanded
8959              * @param {Roo.bootstrap.ComboBox} combo This combo box
8960              */
8961         'expand' : true,
8962         /**
8963          * @event collapse
8964          * Fires when the dropdown list is collapsed
8965              * @param {Roo.bootstrap.ComboBox} combo This combo box
8966              */
8967         'collapse' : true,
8968         /**
8969          * @event beforeselect
8970          * Fires before a list item is selected. Return false to cancel the selection.
8971              * @param {Roo.bootstrap.ComboBox} combo This combo box
8972              * @param {Roo.data.Record} record The data record returned from the underlying store
8973              * @param {Number} index The index of the selected item in the dropdown list
8974              */
8975         'beforeselect' : true,
8976         /**
8977          * @event select
8978          * Fires when a list item is selected
8979              * @param {Roo.bootstrap.ComboBox} combo This combo box
8980              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
8981              * @param {Number} index The index of the selected item in the dropdown list
8982              */
8983         'select' : true,
8984         /**
8985          * @event beforequery
8986          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
8987          * The event object passed has these properties:
8988              * @param {Roo.bootstrap.ComboBox} combo This combo box
8989              * @param {String} query The query
8990              * @param {Boolean} forceAll true to force "all" query
8991              * @param {Boolean} cancel true to cancel the query
8992              * @param {Object} e The query event object
8993              */
8994         'beforequery': true,
8995          /**
8996          * @event add
8997          * Fires when the 'add' icon is pressed (add a listener to enable add button)
8998              * @param {Roo.bootstrap.ComboBox} combo This combo box
8999              */
9000         'add' : true,
9001         /**
9002          * @event edit
9003          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9004              * @param {Roo.bootstrap.ComboBox} combo This combo box
9005              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9006              */
9007         'edit' : true,
9008         /**
9009          * @event remove
9010          * Fires when the remove value from the combobox array
9011              * @param {Roo.bootstrap.ComboBox} combo This combo box
9012              */
9013         'remove' : true
9014         
9015     });
9016     
9017     
9018     this.selectedIndex = -1;
9019     if(this.mode == 'local'){
9020         if(config.queryDelay === undefined){
9021             this.queryDelay = 10;
9022         }
9023         if(config.minChars === undefined){
9024             this.minChars = 0;
9025         }
9026     }
9027 };
9028
9029 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
9030      
9031     /**
9032      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
9033      * rendering into an Roo.Editor, defaults to false)
9034      */
9035     /**
9036      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
9037      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
9038      */
9039     /**
9040      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
9041      */
9042     /**
9043      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
9044      * the dropdown list (defaults to undefined, with no header element)
9045      */
9046
9047      /**
9048      * @cfg {String/Roo.Template} tpl The template to use to render the output
9049      */
9050      
9051      /**
9052      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
9053      */
9054     listWidth: undefined,
9055     /**
9056      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
9057      * mode = 'remote' or 'text' if mode = 'local')
9058      */
9059     displayField: undefined,
9060     /**
9061      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
9062      * mode = 'remote' or 'value' if mode = 'local'). 
9063      * Note: use of a valueField requires the user make a selection
9064      * in order for a value to be mapped.
9065      */
9066     valueField: undefined,
9067     
9068     
9069     /**
9070      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
9071      * field's data value (defaults to the underlying DOM element's name)
9072      */
9073     hiddenName: undefined,
9074     /**
9075      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
9076      */
9077     listClass: '',
9078     /**
9079      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
9080      */
9081     selectedClass: 'active',
9082     
9083     /**
9084      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9085      */
9086     shadow:'sides',
9087     /**
9088      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
9089      * anchor positions (defaults to 'tl-bl')
9090      */
9091     listAlign: 'tl-bl?',
9092     /**
9093      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
9094      */
9095     maxHeight: 300,
9096     /**
9097      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
9098      * query specified by the allQuery config option (defaults to 'query')
9099      */
9100     triggerAction: 'query',
9101     /**
9102      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
9103      * (defaults to 4, does not apply if editable = false)
9104      */
9105     minChars : 4,
9106     /**
9107      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
9108      * delay (typeAheadDelay) if it matches a known value (defaults to false)
9109      */
9110     typeAhead: false,
9111     /**
9112      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
9113      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
9114      */
9115     queryDelay: 500,
9116     /**
9117      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
9118      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
9119      */
9120     pageSize: 0,
9121     /**
9122      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
9123      * when editable = true (defaults to false)
9124      */
9125     selectOnFocus:false,
9126     /**
9127      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
9128      */
9129     queryParam: 'query',
9130     /**
9131      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
9132      * when mode = 'remote' (defaults to 'Loading...')
9133      */
9134     loadingText: 'Loading...',
9135     /**
9136      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
9137      */
9138     resizable: false,
9139     /**
9140      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
9141      */
9142     handleHeight : 8,
9143     /**
9144      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
9145      * traditional select (defaults to true)
9146      */
9147     editable: true,
9148     /**
9149      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
9150      */
9151     allQuery: '',
9152     /**
9153      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
9154      */
9155     mode: 'remote',
9156     /**
9157      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
9158      * listWidth has a higher value)
9159      */
9160     minListWidth : 70,
9161     /**
9162      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
9163      * allow the user to set arbitrary text into the field (defaults to false)
9164      */
9165     forceSelection:false,
9166     /**
9167      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
9168      * if typeAhead = true (defaults to 250)
9169      */
9170     typeAheadDelay : 250,
9171     /**
9172      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
9173      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
9174      */
9175     valueNotFoundText : undefined,
9176     /**
9177      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
9178      */
9179     blockFocus : false,
9180     
9181     /**
9182      * @cfg {Boolean} disableClear Disable showing of clear button.
9183      */
9184     disableClear : false,
9185     /**
9186      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
9187      */
9188     alwaysQuery : false,
9189     
9190     /**
9191      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
9192      */
9193     multiple : false,
9194     
9195     //private
9196     addicon : false,
9197     editicon: false,
9198     
9199     page: 0,
9200     hasQuery: false,
9201     append: false,
9202     loadNext: false,
9203     item: [],
9204     
9205     // element that contains real text value.. (when hidden is used..)
9206      
9207     // private
9208     initEvents: function(){
9209         
9210         if (!this.store) {
9211             throw "can not find store for combo";
9212         }
9213         this.store = Roo.factory(this.store, Roo.data);
9214         
9215         
9216         
9217         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
9218         
9219         
9220         if(this.hiddenName){
9221             
9222             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
9223             
9224             this.hiddenField.dom.value =
9225                 this.hiddenValue !== undefined ? this.hiddenValue :
9226                 this.value !== undefined ? this.value : '';
9227
9228             // prevent input submission
9229             this.el.dom.removeAttribute('name');
9230             this.hiddenField.dom.setAttribute('name', this.hiddenName);
9231              
9232              
9233         }
9234         //if(Roo.isGecko){
9235         //    this.el.dom.setAttribute('autocomplete', 'off');
9236         //}
9237
9238         var cls = 'x-combo-list';
9239         this.list = this.el.select('ul.dropdown-menu',true).first();
9240
9241         //this.list = new Roo.Layer({
9242         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
9243         //});
9244         
9245         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
9246         this.list.setWidth(lw);
9247         
9248         this.list.on('mouseover', this.onViewOver, this);
9249         this.list.on('mousemove', this.onViewMove, this);
9250         
9251         this.list.on('scroll', this.onViewScroll, this);
9252         
9253         /*
9254         this.list.swallowEvent('mousewheel');
9255         this.assetHeight = 0;
9256
9257         if(this.title){
9258             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
9259             this.assetHeight += this.header.getHeight();
9260         }
9261
9262         this.innerList = this.list.createChild({cls:cls+'-inner'});
9263         this.innerList.on('mouseover', this.onViewOver, this);
9264         this.innerList.on('mousemove', this.onViewMove, this);
9265         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9266         
9267         if(this.allowBlank && !this.pageSize && !this.disableClear){
9268             this.footer = this.list.createChild({cls:cls+'-ft'});
9269             this.pageTb = new Roo.Toolbar(this.footer);
9270            
9271         }
9272         if(this.pageSize){
9273             this.footer = this.list.createChild({cls:cls+'-ft'});
9274             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
9275                     {pageSize: this.pageSize});
9276             
9277         }
9278         
9279         if (this.pageTb && this.allowBlank && !this.disableClear) {
9280             var _this = this;
9281             this.pageTb.add(new Roo.Toolbar.Fill(), {
9282                 cls: 'x-btn-icon x-btn-clear',
9283                 text: '&#160;',
9284                 handler: function()
9285                 {
9286                     _this.collapse();
9287                     _this.clearValue();
9288                     _this.onSelect(false, -1);
9289                 }
9290             });
9291         }
9292         if (this.footer) {
9293             this.assetHeight += this.footer.getHeight();
9294         }
9295         */
9296             
9297         if(!this.tpl){
9298             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
9299         }
9300
9301         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
9302             singleSelect:true, store: this.store, selectedClass: this.selectedClass
9303         });
9304         //this.view.wrapEl.setDisplayed(false);
9305         this.view.on('click', this.onViewClick, this);
9306         
9307         
9308         
9309         this.store.on('beforeload', this.onBeforeLoad, this);
9310         this.store.on('load', this.onLoad, this);
9311         this.store.on('loadexception', this.onLoadException, this);
9312         /*
9313         if(this.resizable){
9314             this.resizer = new Roo.Resizable(this.list,  {
9315                pinned:true, handles:'se'
9316             });
9317             this.resizer.on('resize', function(r, w, h){
9318                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
9319                 this.listWidth = w;
9320                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
9321                 this.restrictHeight();
9322             }, this);
9323             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
9324         }
9325         */
9326         if(!this.editable){
9327             this.editable = true;
9328             this.setEditable(false);
9329         }
9330         
9331         /*
9332         
9333         if (typeof(this.events.add.listeners) != 'undefined') {
9334             
9335             this.addicon = this.wrap.createChild(
9336                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
9337        
9338             this.addicon.on('click', function(e) {
9339                 this.fireEvent('add', this);
9340             }, this);
9341         }
9342         if (typeof(this.events.edit.listeners) != 'undefined') {
9343             
9344             this.editicon = this.wrap.createChild(
9345                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
9346             if (this.addicon) {
9347                 this.editicon.setStyle('margin-left', '40px');
9348             }
9349             this.editicon.on('click', function(e) {
9350                 
9351                 // we fire even  if inothing is selected..
9352                 this.fireEvent('edit', this, this.lastData );
9353                 
9354             }, this);
9355         }
9356         */
9357         
9358         this.keyNav = new Roo.KeyNav(this.inputEl(), {
9359             "up" : function(e){
9360                 this.inKeyMode = true;
9361                 this.selectPrev();
9362             },
9363
9364             "down" : function(e){
9365                 if(!this.isExpanded()){
9366                     this.onTriggerClick();
9367                 }else{
9368                     this.inKeyMode = true;
9369                     this.selectNext();
9370                 }
9371             },
9372
9373             "enter" : function(e){
9374                 this.onViewClick();
9375                 //return true;
9376             },
9377
9378             "esc" : function(e){
9379                 this.collapse();
9380             },
9381
9382             "tab" : function(e){
9383                 this.collapse();
9384                 
9385                 if(this.fireEvent("specialkey", this, e)){
9386                     this.onViewClick(false);
9387                 }
9388                 
9389                 return true;
9390             },
9391
9392             scope : this,
9393
9394             doRelay : function(foo, bar, hname){
9395                 if(hname == 'down' || this.scope.isExpanded()){
9396                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
9397                 }
9398                 return true;
9399             },
9400
9401             forceKeyDown: true
9402         });
9403         
9404         
9405         this.queryDelay = Math.max(this.queryDelay || 10,
9406                 this.mode == 'local' ? 10 : 250);
9407         
9408         
9409         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
9410         
9411         if(this.typeAhead){
9412             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
9413         }
9414         if(this.editable !== false){
9415             this.inputEl().on("keyup", this.onKeyUp, this);
9416         }
9417         if(this.forceSelection){
9418             this.inputEl().on('blur', this.doForce, this);
9419         }
9420         
9421         if(this.multiple){
9422             this.choices = this.el.select('ul.select2-choices', true).first();
9423             this.searchField = this.el.select('ul li.select2-search-field', true).first();
9424         }
9425     },
9426
9427     onDestroy : function(){
9428         if(this.view){
9429             this.view.setStore(null);
9430             this.view.el.removeAllListeners();
9431             this.view.el.remove();
9432             this.view.purgeListeners();
9433         }
9434         if(this.list){
9435             this.list.dom.innerHTML  = '';
9436         }
9437         if(this.store){
9438             this.store.un('beforeload', this.onBeforeLoad, this);
9439             this.store.un('load', this.onLoad, this);
9440             this.store.un('loadexception', this.onLoadException, this);
9441         }
9442         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9443     },
9444
9445     // private
9446     fireKey : function(e){
9447         if(e.isNavKeyPress() && !this.list.isVisible()){
9448             this.fireEvent("specialkey", this, e);
9449         }
9450     },
9451
9452     // private
9453     onResize: function(w, h){
9454 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9455 //        
9456 //        if(typeof w != 'number'){
9457 //            // we do not handle it!?!?
9458 //            return;
9459 //        }
9460 //        var tw = this.trigger.getWidth();
9461 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
9462 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
9463 //        var x = w - tw;
9464 //        this.inputEl().setWidth( this.adjustWidth('input', x));
9465 //            
9466 //        //this.trigger.setStyle('left', x+'px');
9467 //        
9468 //        if(this.list && this.listWidth === undefined){
9469 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9470 //            this.list.setWidth(lw);
9471 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9472 //        }
9473         
9474     
9475         
9476     },
9477
9478     /**
9479      * Allow or prevent the user from directly editing the field text.  If false is passed,
9480      * the user will only be able to select from the items defined in the dropdown list.  This method
9481      * is the runtime equivalent of setting the 'editable' config option at config time.
9482      * @param {Boolean} value True to allow the user to directly edit the field text
9483      */
9484     setEditable : function(value){
9485         if(value == this.editable){
9486             return;
9487         }
9488         this.editable = value;
9489         if(!value){
9490             this.inputEl().dom.setAttribute('readOnly', true);
9491             this.inputEl().on('mousedown', this.onTriggerClick,  this);
9492             this.inputEl().addClass('x-combo-noedit');
9493         }else{
9494             this.inputEl().dom.setAttribute('readOnly', false);
9495             this.inputEl().un('mousedown', this.onTriggerClick,  this);
9496             this.inputEl().removeClass('x-combo-noedit');
9497         }
9498     },
9499
9500     // private
9501     
9502     onBeforeLoad : function(combo,opts){
9503         if(!this.hasFocus){
9504             return;
9505         }
9506          if (!opts.add) {
9507             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9508          }
9509         this.restrictHeight();
9510         this.selectedIndex = -1;
9511     },
9512
9513     // private
9514     onLoad : function(){
9515         
9516         this.hasQuery = false;
9517         
9518         if(!this.hasFocus){
9519             return;
9520         }
9521         
9522         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9523             this.loading.hide();
9524         }
9525         
9526         if(this.store.getCount() > 0){
9527             this.expand();
9528             this.restrictHeight();
9529             if(this.lastQuery == this.allQuery){
9530                 if(this.editable){
9531                     this.inputEl().dom.select();
9532                 }
9533                 if(!this.selectByValue(this.value, true)){
9534                     this.select(0, true);
9535                 }
9536             }else{
9537                 this.selectNext();
9538                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9539                     this.taTask.delay(this.typeAheadDelay);
9540                 }
9541             }
9542         }else{
9543             this.onEmptyResults();
9544         }
9545         
9546         //this.el.focus();
9547     },
9548     // private
9549     onLoadException : function()
9550     {
9551         this.hasQuery = false;
9552         
9553         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9554             this.loading.hide();
9555         }
9556         
9557         this.collapse();
9558         Roo.log(this.store.reader.jsonData);
9559         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9560             // fixme
9561             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9562         }
9563         
9564         
9565     },
9566     // private
9567     onTypeAhead : function(){
9568         if(this.store.getCount() > 0){
9569             var r = this.store.getAt(0);
9570             var newValue = r.data[this.displayField];
9571             var len = newValue.length;
9572             var selStart = this.getRawValue().length;
9573             
9574             if(selStart != len){
9575                 this.setRawValue(newValue);
9576                 this.selectText(selStart, newValue.length);
9577             }
9578         }
9579     },
9580
9581     // private
9582     onSelect : function(record, index){
9583         
9584         if(this.fireEvent('beforeselect', this, record, index) !== false){
9585         
9586             this.setFromData(index > -1 ? record.data : false);
9587             
9588             this.collapse();
9589             this.fireEvent('select', this, record, index);
9590         }
9591     },
9592
9593     /**
9594      * Returns the currently selected field value or empty string if no value is set.
9595      * @return {String} value The selected value
9596      */
9597     getValue : function(){
9598         
9599         if(this.multiple){
9600             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9601         }
9602         
9603         if(this.valueField){
9604             return typeof this.value != 'undefined' ? this.value : '';
9605         }else{
9606             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9607         }
9608     },
9609
9610     /**
9611      * Clears any text/value currently set in the field
9612      */
9613     clearValue : function(){
9614         if(this.hiddenField){
9615             this.hiddenField.dom.value = '';
9616         }
9617         this.value = '';
9618         this.setRawValue('');
9619         this.lastSelectionText = '';
9620         
9621     },
9622
9623     /**
9624      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
9625      * will be displayed in the field.  If the value does not match the data value of an existing item,
9626      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9627      * Otherwise the field will be blank (although the value will still be set).
9628      * @param {String} value The value to match
9629      */
9630     setValue : function(v){
9631         if(this.multiple){
9632             this.syncValue();
9633             return;
9634         }
9635         
9636         var text = v;
9637         if(this.valueField){
9638             var r = this.findRecord(this.valueField, v);
9639             if(r){
9640                 text = r.data[this.displayField];
9641             }else if(this.valueNotFoundText !== undefined){
9642                 text = this.valueNotFoundText;
9643             }
9644         }
9645         this.lastSelectionText = text;
9646         if(this.hiddenField){
9647             this.hiddenField.dom.value = v;
9648         }
9649         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9650         this.value = v;
9651     },
9652     /**
9653      * @property {Object} the last set data for the element
9654      */
9655     
9656     lastData : false,
9657     /**
9658      * Sets the value of the field based on a object which is related to the record format for the store.
9659      * @param {Object} value the value to set as. or false on reset?
9660      */
9661     setFromData : function(o){
9662         
9663         if(this.multiple){
9664             this.addItem(o);
9665             return;
9666         }
9667             
9668         var dv = ''; // display value
9669         var vv = ''; // value value..
9670         this.lastData = o;
9671         if (this.displayField) {
9672             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9673         } else {
9674             // this is an error condition!!!
9675             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9676         }
9677         
9678         if(this.valueField){
9679             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9680         }
9681         
9682         if(this.hiddenField){
9683             this.hiddenField.dom.value = vv;
9684             
9685             this.lastSelectionText = dv;
9686             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9687             this.value = vv;
9688             return;
9689         }
9690         // no hidden field.. - we store the value in 'value', but still display
9691         // display field!!!!
9692         this.lastSelectionText = dv;
9693         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9694         this.value = vv;
9695         
9696         
9697     },
9698     // private
9699     reset : function(){
9700         // overridden so that last data is reset..
9701         this.setValue(this.originalValue);
9702         this.clearInvalid();
9703         this.lastData = false;
9704         if (this.view) {
9705             this.view.clearSelections();
9706         }
9707     },
9708     // private
9709     findRecord : function(prop, value){
9710         var record;
9711         if(this.store.getCount() > 0){
9712             this.store.each(function(r){
9713                 if(r.data[prop] == value){
9714                     record = r;
9715                     return false;
9716                 }
9717                 return true;
9718             });
9719         }
9720         return record;
9721     },
9722     
9723     getName: function()
9724     {
9725         // returns hidden if it's set..
9726         if (!this.rendered) {return ''};
9727         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
9728         
9729     },
9730     // private
9731     onViewMove : function(e, t){
9732         this.inKeyMode = false;
9733     },
9734
9735     // private
9736     onViewOver : function(e, t){
9737         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9738             return;
9739         }
9740         var item = this.view.findItemFromChild(t);
9741         if(item){
9742             var index = this.view.indexOf(item);
9743             this.select(index, false);
9744         }
9745     },
9746
9747     // private
9748     onViewClick : function(doFocus)
9749     {
9750         var index = this.view.getSelectedIndexes()[0];
9751         var r = this.store.getAt(index);
9752         if(r){
9753             this.onSelect(r, index);
9754         }
9755         if(doFocus !== false && !this.blockFocus){
9756             this.inputEl().focus();
9757         }
9758     },
9759
9760     // private
9761     restrictHeight : function(){
9762         //this.innerList.dom.style.height = '';
9763         //var inner = this.innerList.dom;
9764         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9765         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9766         //this.list.beginUpdate();
9767         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9768         this.list.alignTo(this.inputEl(), this.listAlign);
9769         //this.list.endUpdate();
9770     },
9771
9772     // private
9773     onEmptyResults : function(){
9774         this.collapse();
9775     },
9776
9777     /**
9778      * Returns true if the dropdown list is expanded, else false.
9779      */
9780     isExpanded : function(){
9781         return this.list.isVisible();
9782     },
9783
9784     /**
9785      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9786      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9787      * @param {String} value The data value of the item to select
9788      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9789      * selected item if it is not currently in view (defaults to true)
9790      * @return {Boolean} True if the value matched an item in the list, else false
9791      */
9792     selectByValue : function(v, scrollIntoView){
9793         if(v !== undefined && v !== null){
9794             var r = this.findRecord(this.valueField || this.displayField, v);
9795             if(r){
9796                 this.select(this.store.indexOf(r), scrollIntoView);
9797                 return true;
9798             }
9799         }
9800         return false;
9801     },
9802
9803     /**
9804      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9805      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9806      * @param {Number} index The zero-based index of the list item to select
9807      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9808      * selected item if it is not currently in view (defaults to true)
9809      */
9810     select : function(index, scrollIntoView){
9811         this.selectedIndex = index;
9812         this.view.select(index);
9813         if(scrollIntoView !== false){
9814             var el = this.view.getNode(index);
9815             if(el){
9816                 //this.innerList.scrollChildIntoView(el, false);
9817                 
9818             }
9819         }
9820     },
9821
9822     // private
9823     selectNext : function(){
9824         var ct = this.store.getCount();
9825         if(ct > 0){
9826             if(this.selectedIndex == -1){
9827                 this.select(0);
9828             }else if(this.selectedIndex < ct-1){
9829                 this.select(this.selectedIndex+1);
9830             }
9831         }
9832     },
9833
9834     // private
9835     selectPrev : function(){
9836         var ct = this.store.getCount();
9837         if(ct > 0){
9838             if(this.selectedIndex == -1){
9839                 this.select(0);
9840             }else if(this.selectedIndex != 0){
9841                 this.select(this.selectedIndex-1);
9842             }
9843         }
9844     },
9845
9846     // private
9847     onKeyUp : function(e){
9848         if(this.editable !== false && !e.isSpecialKey()){
9849             this.lastKey = e.getKey();
9850             this.dqTask.delay(this.queryDelay);
9851         }
9852     },
9853
9854     // private
9855     validateBlur : function(){
9856         return !this.list || !this.list.isVisible();   
9857     },
9858
9859     // private
9860     initQuery : function(){
9861         this.doQuery(this.getRawValue());
9862     },
9863
9864     // private
9865     doForce : function(){
9866         if(this.inputEl().dom.value.length > 0){
9867             this.inputEl().dom.value =
9868                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
9869              
9870         }
9871     },
9872
9873     /**
9874      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
9875      * query allowing the query action to be canceled if needed.
9876      * @param {String} query The SQL query to execute
9877      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
9878      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
9879      * saved in the current store (defaults to false)
9880      */
9881     doQuery : function(q, forceAll){
9882         
9883         if(q === undefined || q === null){
9884             q = '';
9885         }
9886         var qe = {
9887             query: q,
9888             forceAll: forceAll,
9889             combo: this,
9890             cancel:false
9891         };
9892         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
9893             return false;
9894         }
9895         q = qe.query;
9896         
9897         forceAll = qe.forceAll;
9898         if(forceAll === true || (q.length >= this.minChars)){
9899             
9900             this.hasQuery = true;
9901             
9902             if(this.lastQuery != q || this.alwaysQuery){
9903                 this.lastQuery = q;
9904                 if(this.mode == 'local'){
9905                     this.selectedIndex = -1;
9906                     if(forceAll){
9907                         this.store.clearFilter();
9908                     }else{
9909                         this.store.filter(this.displayField, q);
9910                     }
9911                     this.onLoad();
9912                 }else{
9913                     this.store.baseParams[this.queryParam] = q;
9914                     
9915                     var options = {params : this.getParams(q)};
9916                     
9917                     if(this.loadNext){
9918                         options.add = true;
9919                         options.params.start = this.page * this.pageSize;
9920                     }
9921                     
9922                     this.store.load(options);
9923                     this.expand();
9924                 }
9925             }else{
9926                 this.selectedIndex = -1;
9927                 this.onLoad();   
9928             }
9929         }
9930         
9931         this.loadNext = false;
9932     },
9933
9934     // private
9935     getParams : function(q){
9936         var p = {};
9937         //p[this.queryParam] = q;
9938         
9939         if(this.pageSize){
9940             p.start = 0;
9941             p.limit = this.pageSize;
9942         }
9943         return p;
9944     },
9945
9946     /**
9947      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
9948      */
9949     collapse : function(){
9950         if(!this.isExpanded()){
9951             return;
9952         }
9953         
9954         this.list.hide();
9955         Roo.get(document).un('mousedown', this.collapseIf, this);
9956         Roo.get(document).un('mousewheel', this.collapseIf, this);
9957         if (!this.editable) {
9958             Roo.get(document).un('keydown', this.listKeyPress, this);
9959         }
9960         this.fireEvent('collapse', this);
9961     },
9962
9963     // private
9964     collapseIf : function(e){
9965         var in_combo  = e.within(this.el);
9966         var in_list =  e.within(this.list);
9967         
9968         if (in_combo || in_list) {
9969             //e.stopPropagation();
9970             return;
9971         }
9972
9973         this.collapse();
9974         
9975     },
9976
9977     /**
9978      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
9979      */
9980     expand : function(){
9981        
9982         if(this.isExpanded() || !this.hasFocus){
9983             return;
9984         }
9985          Roo.log('expand');
9986         this.list.alignTo(this.inputEl(), this.listAlign);
9987         this.list.show();
9988         Roo.get(document).on('mousedown', this.collapseIf, this);
9989         Roo.get(document).on('mousewheel', this.collapseIf, this);
9990         if (!this.editable) {
9991             Roo.get(document).on('keydown', this.listKeyPress, this);
9992         }
9993         
9994         this.fireEvent('expand', this);
9995     },
9996
9997     // private
9998     // Implements the default empty TriggerField.onTriggerClick function
9999     onTriggerClick : function()
10000     {
10001         Roo.log('trigger click');
10002         
10003         if(this.disabled){
10004             return;
10005         }
10006         
10007         this.page = 0;
10008         this.loadNext = false;
10009         
10010         if(this.isExpanded()){
10011             this.collapse();
10012             if (!this.blockFocus) {
10013                 this.inputEl().focus();
10014             }
10015             
10016         }else {
10017             this.hasFocus = true;
10018             if(this.triggerAction == 'all') {
10019                 this.doQuery(this.allQuery, true);
10020             } else {
10021                 this.doQuery(this.getRawValue());
10022             }
10023             if (!this.blockFocus) {
10024                 this.inputEl().focus();
10025             }
10026         }
10027     },
10028     listKeyPress : function(e)
10029     {
10030         //Roo.log('listkeypress');
10031         // scroll to first matching element based on key pres..
10032         if (e.isSpecialKey()) {
10033             return false;
10034         }
10035         var k = String.fromCharCode(e.getKey()).toUpperCase();
10036         //Roo.log(k);
10037         var match  = false;
10038         var csel = this.view.getSelectedNodes();
10039         var cselitem = false;
10040         if (csel.length) {
10041             var ix = this.view.indexOf(csel[0]);
10042             cselitem  = this.store.getAt(ix);
10043             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
10044                 cselitem = false;
10045             }
10046             
10047         }
10048         
10049         this.store.each(function(v) { 
10050             if (cselitem) {
10051                 // start at existing selection.
10052                 if (cselitem.id == v.id) {
10053                     cselitem = false;
10054                 }
10055                 return true;
10056             }
10057                 
10058             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
10059                 match = this.store.indexOf(v);
10060                 return false;
10061             }
10062             return true;
10063         }, this);
10064         
10065         if (match === false) {
10066             return true; // no more action?
10067         }
10068         // scroll to?
10069         this.view.select(match);
10070         var sn = Roo.get(this.view.getSelectedNodes()[0])
10071         //sn.scrollIntoView(sn.dom.parentNode, false);
10072     },
10073     
10074     onViewScroll : function(e, t){
10075         
10076         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
10077             return;
10078         }
10079         
10080         this.hasQuery = true;
10081         
10082         this.loading = this.list.select('.loading', true).first();
10083         
10084         if(this.loading === null){
10085             this.list.createChild({
10086                 tag: 'div',
10087                 cls: 'loading select2-more-results select2-active',
10088                 html: 'Loading more results...'
10089             })
10090             
10091             this.loading = this.list.select('.loading', true).first();
10092             
10093             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
10094             
10095             this.loading.hide();
10096         }
10097         
10098         this.loading.show();
10099         
10100         var _combo = this;
10101         
10102         this.page++;
10103         this.loadNext = true;
10104         
10105         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
10106         
10107         return;
10108     },
10109     
10110     addItem : function(o)
10111     {   
10112         var dv = ''; // display value
10113         
10114         if (this.displayField) {
10115             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10116         } else {
10117             // this is an error condition!!!
10118             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10119         }
10120         
10121         if(!dv.length){
10122             return;
10123         }
10124         
10125         var choice = this.choices.createChild({
10126             tag: 'li',
10127             cls: 'select2-search-choice',
10128             cn: [
10129                 {
10130                     tag: 'div',
10131                     html: dv
10132                 },
10133                 {
10134                     tag: 'a',
10135                     href: '#',
10136                     cls: 'select2-search-choice-close',
10137                     tabindex: '-1'
10138                 }
10139             ]
10140             
10141         }, this.searchField);
10142         
10143         var close = choice.select('a.select2-search-choice-close', true).first()
10144         
10145         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
10146         
10147         this.item.push(o);
10148         this.lastData = o;
10149         
10150         this.syncValue();
10151         
10152         this.inputEl().dom.value = '';
10153         
10154     },
10155     
10156     onRemoveItem : function(e, _self, o)
10157     {
10158         e.preventDefault();
10159         var index = this.item.indexOf(o.data) * 1;
10160         
10161         if( index < 0){
10162             Roo.log('not this item?!');
10163             return;
10164         }
10165         
10166         this.item.splice(index, 1);
10167         o.item.remove();
10168         
10169         this.syncValue();
10170         
10171         this.fireEvent('remove', this, e);
10172         
10173     },
10174     
10175     syncValue : function()
10176     {
10177         if(!this.item.length){
10178             this.clearValue();
10179             return;
10180         }
10181             
10182         var value = [];
10183         var _this = this;
10184         Roo.each(this.item, function(i){
10185             if(_this.valueField){
10186                 value.push(i[_this.valueField]);
10187                 return;
10188             }
10189
10190             value.push(i);
10191         });
10192
10193         this.value = value.join(',');
10194
10195         if(this.hiddenField){
10196             this.hiddenField.dom.value = this.value;
10197         }
10198     },
10199     
10200     clearItem : function()
10201     {
10202         if(!this.multiple){
10203             return;
10204         }
10205         
10206         this.item = [];
10207         
10208         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
10209            c.remove();
10210         });
10211         
10212         this.syncValue();
10213     }
10214     
10215     
10216
10217     /** 
10218     * @cfg {Boolean} grow 
10219     * @hide 
10220     */
10221     /** 
10222     * @cfg {Number} growMin 
10223     * @hide 
10224     */
10225     /** 
10226     * @cfg {Number} growMax 
10227     * @hide 
10228     */
10229     /**
10230      * @hide
10231      * @method autoSize
10232      */
10233 });
10234 /*
10235  * Based on:
10236  * Ext JS Library 1.1.1
10237  * Copyright(c) 2006-2007, Ext JS, LLC.
10238  *
10239  * Originally Released Under LGPL - original licence link has changed is not relivant.
10240  *
10241  * Fork - LGPL
10242  * <script type="text/javascript">
10243  */
10244
10245 /**
10246  * @class Roo.View
10247  * @extends Roo.util.Observable
10248  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
10249  * This class also supports single and multi selection modes. <br>
10250  * Create a data model bound view:
10251  <pre><code>
10252  var store = new Roo.data.Store(...);
10253
10254  var view = new Roo.View({
10255     el : "my-element",
10256     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
10257  
10258     singleSelect: true,
10259     selectedClass: "ydataview-selected",
10260     store: store
10261  });
10262
10263  // listen for node click?
10264  view.on("click", function(vw, index, node, e){
10265  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
10266  });
10267
10268  // load XML data
10269  dataModel.load("foobar.xml");
10270  </code></pre>
10271  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
10272  * <br><br>
10273  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
10274  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
10275  * 
10276  * Note: old style constructor is still suported (container, template, config)
10277  * 
10278  * @constructor
10279  * Create a new View
10280  * @param {Object} config The config object
10281  * 
10282  */
10283 Roo.View = function(config, depreciated_tpl, depreciated_config){
10284     
10285     if (typeof(depreciated_tpl) == 'undefined') {
10286         // new way.. - universal constructor.
10287         Roo.apply(this, config);
10288         this.el  = Roo.get(this.el);
10289     } else {
10290         // old format..
10291         this.el  = Roo.get(config);
10292         this.tpl = depreciated_tpl;
10293         Roo.apply(this, depreciated_config);
10294     }
10295     this.wrapEl  = this.el.wrap().wrap();
10296     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
10297     
10298     
10299     if(typeof(this.tpl) == "string"){
10300         this.tpl = new Roo.Template(this.tpl);
10301     } else {
10302         // support xtype ctors..
10303         this.tpl = new Roo.factory(this.tpl, Roo);
10304     }
10305     
10306     
10307     this.tpl.compile();
10308    
10309   
10310     
10311      
10312     /** @private */
10313     this.addEvents({
10314         /**
10315          * @event beforeclick
10316          * Fires before a click is processed. Returns false to cancel the default action.
10317          * @param {Roo.View} this
10318          * @param {Number} index The index of the target node
10319          * @param {HTMLElement} node The target node
10320          * @param {Roo.EventObject} e The raw event object
10321          */
10322             "beforeclick" : true,
10323         /**
10324          * @event click
10325          * Fires when a template node is clicked.
10326          * @param {Roo.View} this
10327          * @param {Number} index The index of the target node
10328          * @param {HTMLElement} node The target node
10329          * @param {Roo.EventObject} e The raw event object
10330          */
10331             "click" : true,
10332         /**
10333          * @event dblclick
10334          * Fires when a template node is double clicked.
10335          * @param {Roo.View} this
10336          * @param {Number} index The index of the target node
10337          * @param {HTMLElement} node The target node
10338          * @param {Roo.EventObject} e The raw event object
10339          */
10340             "dblclick" : true,
10341         /**
10342          * @event contextmenu
10343          * Fires when a template node is right clicked.
10344          * @param {Roo.View} this
10345          * @param {Number} index The index of the target node
10346          * @param {HTMLElement} node The target node
10347          * @param {Roo.EventObject} e The raw event object
10348          */
10349             "contextmenu" : true,
10350         /**
10351          * @event selectionchange
10352          * Fires when the selected nodes change.
10353          * @param {Roo.View} this
10354          * @param {Array} selections Array of the selected nodes
10355          */
10356             "selectionchange" : true,
10357     
10358         /**
10359          * @event beforeselect
10360          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
10361          * @param {Roo.View} this
10362          * @param {HTMLElement} node The node to be selected
10363          * @param {Array} selections Array of currently selected nodes
10364          */
10365             "beforeselect" : true,
10366         /**
10367          * @event preparedata
10368          * Fires on every row to render, to allow you to change the data.
10369          * @param {Roo.View} this
10370          * @param {Object} data to be rendered (change this)
10371          */
10372           "preparedata" : true
10373           
10374           
10375         });
10376
10377
10378
10379     this.el.on({
10380         "click": this.onClick,
10381         "dblclick": this.onDblClick,
10382         "contextmenu": this.onContextMenu,
10383         scope:this
10384     });
10385
10386     this.selections = [];
10387     this.nodes = [];
10388     this.cmp = new Roo.CompositeElementLite([]);
10389     if(this.store){
10390         this.store = Roo.factory(this.store, Roo.data);
10391         this.setStore(this.store, true);
10392     }
10393     
10394     if ( this.footer && this.footer.xtype) {
10395            
10396          var fctr = this.wrapEl.appendChild(document.createElement("div"));
10397         
10398         this.footer.dataSource = this.store
10399         this.footer.container = fctr;
10400         this.footer = Roo.factory(this.footer, Roo);
10401         fctr.insertFirst(this.el);
10402         
10403         // this is a bit insane - as the paging toolbar seems to detach the el..
10404 //        dom.parentNode.parentNode.parentNode
10405          // they get detached?
10406     }
10407     
10408     
10409     Roo.View.superclass.constructor.call(this);
10410     
10411     
10412 };
10413
10414 Roo.extend(Roo.View, Roo.util.Observable, {
10415     
10416      /**
10417      * @cfg {Roo.data.Store} store Data store to load data from.
10418      */
10419     store : false,
10420     
10421     /**
10422      * @cfg {String|Roo.Element} el The container element.
10423      */
10424     el : '',
10425     
10426     /**
10427      * @cfg {String|Roo.Template} tpl The template used by this View 
10428      */
10429     tpl : false,
10430     /**
10431      * @cfg {String} dataName the named area of the template to use as the data area
10432      *                          Works with domtemplates roo-name="name"
10433      */
10434     dataName: false,
10435     /**
10436      * @cfg {String} selectedClass The css class to add to selected nodes
10437      */
10438     selectedClass : "x-view-selected",
10439      /**
10440      * @cfg {String} emptyText The empty text to show when nothing is loaded.
10441      */
10442     emptyText : "",
10443     
10444     /**
10445      * @cfg {String} text to display on mask (default Loading)
10446      */
10447     mask : false,
10448     /**
10449      * @cfg {Boolean} multiSelect Allow multiple selection
10450      */
10451     multiSelect : false,
10452     /**
10453      * @cfg {Boolean} singleSelect Allow single selection
10454      */
10455     singleSelect:  false,
10456     
10457     /**
10458      * @cfg {Boolean} toggleSelect - selecting 
10459      */
10460     toggleSelect : false,
10461     
10462     /**
10463      * Returns the element this view is bound to.
10464      * @return {Roo.Element}
10465      */
10466     getEl : function(){
10467         return this.wrapEl;
10468     },
10469     
10470     
10471
10472     /**
10473      * Refreshes the view. - called by datachanged on the store. - do not call directly.
10474      */
10475     refresh : function(){
10476         Roo.log('refresh');
10477         var t = this.tpl;
10478         
10479         // if we are using something like 'domtemplate', then
10480         // the what gets used is:
10481         // t.applySubtemplate(NAME, data, wrapping data..)
10482         // the outer template then get' applied with
10483         //     the store 'extra data'
10484         // and the body get's added to the
10485         //      roo-name="data" node?
10486         //      <span class='roo-tpl-{name}'></span> ?????
10487         
10488         
10489         
10490         this.clearSelections();
10491         this.el.update("");
10492         var html = [];
10493         var records = this.store.getRange();
10494         if(records.length < 1) {
10495             
10496             // is this valid??  = should it render a template??
10497             
10498             this.el.update(this.emptyText);
10499             return;
10500         }
10501         var el = this.el;
10502         if (this.dataName) {
10503             this.el.update(t.apply(this.store.meta)); //????
10504             el = this.el.child('.roo-tpl-' + this.dataName);
10505         }
10506         
10507         for(var i = 0, len = records.length; i < len; i++){
10508             var data = this.prepareData(records[i].data, i, records[i]);
10509             this.fireEvent("preparedata", this, data, i, records[i]);
10510             html[html.length] = Roo.util.Format.trim(
10511                 this.dataName ?
10512                     t.applySubtemplate(this.dataName, data, this.store.meta) :
10513                     t.apply(data)
10514             );
10515         }
10516         
10517         
10518         
10519         el.update(html.join(""));
10520         this.nodes = el.dom.childNodes;
10521         this.updateIndexes(0);
10522     },
10523     
10524
10525     /**
10526      * Function to override to reformat the data that is sent to
10527      * the template for each node.
10528      * DEPRICATED - use the preparedata event handler.
10529      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10530      * a JSON object for an UpdateManager bound view).
10531      */
10532     prepareData : function(data, index, record)
10533     {
10534         this.fireEvent("preparedata", this, data, index, record);
10535         return data;
10536     },
10537
10538     onUpdate : function(ds, record){
10539          Roo.log('on update');   
10540         this.clearSelections();
10541         var index = this.store.indexOf(record);
10542         var n = this.nodes[index];
10543         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10544         n.parentNode.removeChild(n);
10545         this.updateIndexes(index, index);
10546     },
10547
10548     
10549     
10550 // --------- FIXME     
10551     onAdd : function(ds, records, index)
10552     {
10553         Roo.log(['on Add', ds, records, index] );        
10554         this.clearSelections();
10555         if(this.nodes.length == 0){
10556             this.refresh();
10557             return;
10558         }
10559         var n = this.nodes[index];
10560         for(var i = 0, len = records.length; i < len; i++){
10561             var d = this.prepareData(records[i].data, i, records[i]);
10562             if(n){
10563                 this.tpl.insertBefore(n, d);
10564             }else{
10565                 
10566                 this.tpl.append(this.el, d);
10567             }
10568         }
10569         this.updateIndexes(index);
10570     },
10571
10572     onRemove : function(ds, record, index){
10573         Roo.log('onRemove');
10574         this.clearSelections();
10575         var el = this.dataName  ?
10576             this.el.child('.roo-tpl-' + this.dataName) :
10577             this.el; 
10578         
10579         el.dom.removeChild(this.nodes[index]);
10580         this.updateIndexes(index);
10581     },
10582
10583     /**
10584      * Refresh an individual node.
10585      * @param {Number} index
10586      */
10587     refreshNode : function(index){
10588         this.onUpdate(this.store, this.store.getAt(index));
10589     },
10590
10591     updateIndexes : function(startIndex, endIndex){
10592         var ns = this.nodes;
10593         startIndex = startIndex || 0;
10594         endIndex = endIndex || ns.length - 1;
10595         for(var i = startIndex; i <= endIndex; i++){
10596             ns[i].nodeIndex = i;
10597         }
10598     },
10599
10600     /**
10601      * Changes the data store this view uses and refresh the view.
10602      * @param {Store} store
10603      */
10604     setStore : function(store, initial){
10605         if(!initial && this.store){
10606             this.store.un("datachanged", this.refresh);
10607             this.store.un("add", this.onAdd);
10608             this.store.un("remove", this.onRemove);
10609             this.store.un("update", this.onUpdate);
10610             this.store.un("clear", this.refresh);
10611             this.store.un("beforeload", this.onBeforeLoad);
10612             this.store.un("load", this.onLoad);
10613             this.store.un("loadexception", this.onLoad);
10614         }
10615         if(store){
10616           
10617             store.on("datachanged", this.refresh, this);
10618             store.on("add", this.onAdd, this);
10619             store.on("remove", this.onRemove, this);
10620             store.on("update", this.onUpdate, this);
10621             store.on("clear", this.refresh, this);
10622             store.on("beforeload", this.onBeforeLoad, this);
10623             store.on("load", this.onLoad, this);
10624             store.on("loadexception", this.onLoad, this);
10625         }
10626         
10627         if(store){
10628             this.refresh();
10629         }
10630     },
10631     /**
10632      * onbeforeLoad - masks the loading area.
10633      *
10634      */
10635     onBeforeLoad : function(store,opts)
10636     {
10637          Roo.log('onBeforeLoad');   
10638         if (!opts.add) {
10639             this.el.update("");
10640         }
10641         this.el.mask(this.mask ? this.mask : "Loading" ); 
10642     },
10643     onLoad : function ()
10644     {
10645         this.el.unmask();
10646     },
10647     
10648
10649     /**
10650      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10651      * @param {HTMLElement} node
10652      * @return {HTMLElement} The template node
10653      */
10654     findItemFromChild : function(node){
10655         var el = this.dataName  ?
10656             this.el.child('.roo-tpl-' + this.dataName,true) :
10657             this.el.dom; 
10658         
10659         if(!node || node.parentNode == el){
10660                     return node;
10661             }
10662             var p = node.parentNode;
10663             while(p && p != el){
10664             if(p.parentNode == el){
10665                 return p;
10666             }
10667             p = p.parentNode;
10668         }
10669             return null;
10670     },
10671
10672     /** @ignore */
10673     onClick : function(e){
10674         var item = this.findItemFromChild(e.getTarget());
10675         if(item){
10676             var index = this.indexOf(item);
10677             if(this.onItemClick(item, index, e) !== false){
10678                 this.fireEvent("click", this, index, item, e);
10679             }
10680         }else{
10681             this.clearSelections();
10682         }
10683     },
10684
10685     /** @ignore */
10686     onContextMenu : function(e){
10687         var item = this.findItemFromChild(e.getTarget());
10688         if(item){
10689             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10690         }
10691     },
10692
10693     /** @ignore */
10694     onDblClick : function(e){
10695         var item = this.findItemFromChild(e.getTarget());
10696         if(item){
10697             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10698         }
10699     },
10700
10701     onItemClick : function(item, index, e)
10702     {
10703         if(this.fireEvent("beforeclick", this, index, item, e) === false){
10704             return false;
10705         }
10706         if (this.toggleSelect) {
10707             var m = this.isSelected(item) ? 'unselect' : 'select';
10708             Roo.log(m);
10709             var _t = this;
10710             _t[m](item, true, false);
10711             return true;
10712         }
10713         if(this.multiSelect || this.singleSelect){
10714             if(this.multiSelect && e.shiftKey && this.lastSelection){
10715                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10716             }else{
10717                 this.select(item, this.multiSelect && e.ctrlKey);
10718                 this.lastSelection = item;
10719             }
10720             e.preventDefault();
10721         }
10722         return true;
10723     },
10724
10725     /**
10726      * Get the number of selected nodes.
10727      * @return {Number}
10728      */
10729     getSelectionCount : function(){
10730         return this.selections.length;
10731     },
10732
10733     /**
10734      * Get the currently selected nodes.
10735      * @return {Array} An array of HTMLElements
10736      */
10737     getSelectedNodes : function(){
10738         return this.selections;
10739     },
10740
10741     /**
10742      * Get the indexes of the selected nodes.
10743      * @return {Array}
10744      */
10745     getSelectedIndexes : function(){
10746         var indexes = [], s = this.selections;
10747         for(var i = 0, len = s.length; i < len; i++){
10748             indexes.push(s[i].nodeIndex);
10749         }
10750         return indexes;
10751     },
10752
10753     /**
10754      * Clear all selections
10755      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10756      */
10757     clearSelections : function(suppressEvent){
10758         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10759             this.cmp.elements = this.selections;
10760             this.cmp.removeClass(this.selectedClass);
10761             this.selections = [];
10762             if(!suppressEvent){
10763                 this.fireEvent("selectionchange", this, this.selections);
10764             }
10765         }
10766     },
10767
10768     /**
10769      * Returns true if the passed node is selected
10770      * @param {HTMLElement/Number} node The node or node index
10771      * @return {Boolean}
10772      */
10773     isSelected : function(node){
10774         var s = this.selections;
10775         if(s.length < 1){
10776             return false;
10777         }
10778         node = this.getNode(node);
10779         return s.indexOf(node) !== -1;
10780     },
10781
10782     /**
10783      * Selects nodes.
10784      * @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
10785      * @param {Boolean} keepExisting (optional) true to keep existing selections
10786      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10787      */
10788     select : function(nodeInfo, keepExisting, suppressEvent){
10789         if(nodeInfo instanceof Array){
10790             if(!keepExisting){
10791                 this.clearSelections(true);
10792             }
10793             for(var i = 0, len = nodeInfo.length; i < len; i++){
10794                 this.select(nodeInfo[i], true, true);
10795             }
10796             return;
10797         } 
10798         var node = this.getNode(nodeInfo);
10799         if(!node || this.isSelected(node)){
10800             return; // already selected.
10801         }
10802         if(!keepExisting){
10803             this.clearSelections(true);
10804         }
10805         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10806             Roo.fly(node).addClass(this.selectedClass);
10807             this.selections.push(node);
10808             if(!suppressEvent){
10809                 this.fireEvent("selectionchange", this, this.selections);
10810             }
10811         }
10812         
10813         
10814     },
10815       /**
10816      * Unselects nodes.
10817      * @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
10818      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10819      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10820      */
10821     unselect : function(nodeInfo, keepExisting, suppressEvent)
10822     {
10823         if(nodeInfo instanceof Array){
10824             Roo.each(this.selections, function(s) {
10825                 this.unselect(s, nodeInfo);
10826             }, this);
10827             return;
10828         }
10829         var node = this.getNode(nodeInfo);
10830         if(!node || !this.isSelected(node)){
10831             Roo.log("not selected");
10832             return; // not selected.
10833         }
10834         // fireevent???
10835         var ns = [];
10836         Roo.each(this.selections, function(s) {
10837             if (s == node ) {
10838                 Roo.fly(node).removeClass(this.selectedClass);
10839
10840                 return;
10841             }
10842             ns.push(s);
10843         },this);
10844         
10845         this.selections= ns;
10846         this.fireEvent("selectionchange", this, this.selections);
10847     },
10848
10849     /**
10850      * Gets a template node.
10851      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10852      * @return {HTMLElement} The node or null if it wasn't found
10853      */
10854     getNode : function(nodeInfo){
10855         if(typeof nodeInfo == "string"){
10856             return document.getElementById(nodeInfo);
10857         }else if(typeof nodeInfo == "number"){
10858             return this.nodes[nodeInfo];
10859         }
10860         return nodeInfo;
10861     },
10862
10863     /**
10864      * Gets a range template nodes.
10865      * @param {Number} startIndex
10866      * @param {Number} endIndex
10867      * @return {Array} An array of nodes
10868      */
10869     getNodes : function(start, end){
10870         var ns = this.nodes;
10871         start = start || 0;
10872         end = typeof end == "undefined" ? ns.length - 1 : end;
10873         var nodes = [];
10874         if(start <= end){
10875             for(var i = start; i <= end; i++){
10876                 nodes.push(ns[i]);
10877             }
10878         } else{
10879             for(var i = start; i >= end; i--){
10880                 nodes.push(ns[i]);
10881             }
10882         }
10883         return nodes;
10884     },
10885
10886     /**
10887      * Finds the index of the passed node
10888      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10889      * @return {Number} The index of the node or -1
10890      */
10891     indexOf : function(node){
10892         node = this.getNode(node);
10893         if(typeof node.nodeIndex == "number"){
10894             return node.nodeIndex;
10895         }
10896         var ns = this.nodes;
10897         for(var i = 0, len = ns.length; i < len; i++){
10898             if(ns[i] == node){
10899                 return i;
10900             }
10901         }
10902         return -1;
10903     }
10904 });
10905 /*
10906  * - LGPL
10907  *
10908  * based on jquery fullcalendar
10909  * 
10910  */
10911
10912 Roo.bootstrap = Roo.bootstrap || {};
10913 /**
10914  * @class Roo.bootstrap.Calendar
10915  * @extends Roo.bootstrap.Component
10916  * Bootstrap Calendar class
10917  * @cfg {Boolean} loadMask (true|false) default false
10918  * @cfg {Object} header generate the user specific header of the calendar, default false
10919
10920  * @constructor
10921  * Create a new Container
10922  * @param {Object} config The config object
10923  */
10924
10925
10926
10927 Roo.bootstrap.Calendar = function(config){
10928     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
10929      this.addEvents({
10930         /**
10931              * @event select
10932              * Fires when a date is selected
10933              * @param {DatePicker} this
10934              * @param {Date} date The selected date
10935              */
10936         'select': true,
10937         /**
10938              * @event monthchange
10939              * Fires when the displayed month changes 
10940              * @param {DatePicker} this
10941              * @param {Date} date The selected month
10942              */
10943         'monthchange': true,
10944         /**
10945              * @event evententer
10946              * Fires when mouse over an event
10947              * @param {Calendar} this
10948              * @param {event} Event
10949              */
10950         'evententer': true,
10951         /**
10952              * @event eventleave
10953              * Fires when the mouse leaves an
10954              * @param {Calendar} this
10955              * @param {event}
10956              */
10957         'eventleave': true,
10958         /**
10959              * @event eventclick
10960              * Fires when the mouse click an
10961              * @param {Calendar} this
10962              * @param {event}
10963              */
10964         'eventclick': true
10965         
10966     });
10967
10968 };
10969
10970 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
10971     
10972      /**
10973      * @cfg {Number} startDay
10974      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10975      */
10976     startDay : 0,
10977     
10978     loadMask : false,
10979     
10980     header : false,
10981       
10982     getAutoCreate : function(){
10983         
10984         
10985         var fc_button = function(name, corner, style, content ) {
10986             return Roo.apply({},{
10987                 tag : 'span',
10988                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
10989                          (corner.length ?
10990                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
10991                             ''
10992                         ),
10993                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
10994                 unselectable: 'on'
10995             });
10996         };
10997         
10998         var header = {};
10999         
11000         if(!this.header){
11001             header = {
11002                 tag : 'table',
11003                 cls : 'fc-header',
11004                 style : 'width:100%',
11005                 cn : [
11006                     {
11007                         tag: 'tr',
11008                         cn : [
11009                             {
11010                                 tag : 'td',
11011                                 cls : 'fc-header-left',
11012                                 cn : [
11013                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
11014                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
11015                                     { tag: 'span', cls: 'fc-header-space' },
11016                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
11017
11018
11019                                 ]
11020                             },
11021
11022                             {
11023                                 tag : 'td',
11024                                 cls : 'fc-header-center',
11025                                 cn : [
11026                                     {
11027                                         tag: 'span',
11028                                         cls: 'fc-header-title',
11029                                         cn : {
11030                                             tag: 'H2',
11031                                             html : 'month / year'
11032                                         }
11033                                     }
11034
11035                                 ]
11036                             },
11037                             {
11038                                 tag : 'td',
11039                                 cls : 'fc-header-right',
11040                                 cn : [
11041                               /*      fc_button('month', 'left', '', 'month' ),
11042                                     fc_button('week', '', '', 'week' ),
11043                                     fc_button('day', 'right', '', 'day' )
11044                                 */    
11045
11046                                 ]
11047                             }
11048
11049                         ]
11050                     }
11051                 ]
11052             };
11053         }
11054         
11055         header = this.header;
11056         
11057        
11058         var cal_heads = function() {
11059             var ret = [];
11060             // fixme - handle this.
11061             
11062             for (var i =0; i < Date.dayNames.length; i++) {
11063                 var d = Date.dayNames[i];
11064                 ret.push({
11065                     tag: 'th',
11066                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
11067                     html : d.substring(0,3)
11068                 });
11069                 
11070             }
11071             ret[0].cls += ' fc-first';
11072             ret[6].cls += ' fc-last';
11073             return ret;
11074         };
11075         var cal_cell = function(n) {
11076             return  {
11077                 tag: 'td',
11078                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
11079                 cn : [
11080                     {
11081                         cn : [
11082                             {
11083                                 cls: 'fc-day-number',
11084                                 html: 'D'
11085                             },
11086                             {
11087                                 cls: 'fc-day-content',
11088                              
11089                                 cn : [
11090                                      {
11091                                         style: 'position: relative;' // height: 17px;
11092                                     }
11093                                 ]
11094                             }
11095                             
11096                             
11097                         ]
11098                     }
11099                 ]
11100                 
11101             }
11102         };
11103         var cal_rows = function() {
11104             
11105             var ret = []
11106             for (var r = 0; r < 6; r++) {
11107                 var row= {
11108                     tag : 'tr',
11109                     cls : 'fc-week',
11110                     cn : []
11111                 };
11112                 
11113                 for (var i =0; i < Date.dayNames.length; i++) {
11114                     var d = Date.dayNames[i];
11115                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
11116
11117                 }
11118                 row.cn[0].cls+=' fc-first';
11119                 row.cn[0].cn[0].style = 'min-height:90px';
11120                 row.cn[6].cls+=' fc-last';
11121                 ret.push(row);
11122                 
11123             }
11124             ret[0].cls += ' fc-first';
11125             ret[4].cls += ' fc-prev-last';
11126             ret[5].cls += ' fc-last';
11127             return ret;
11128             
11129         };
11130         
11131         var cal_table = {
11132             tag: 'table',
11133             cls: 'fc-border-separate',
11134             style : 'width:100%',
11135             cellspacing  : 0,
11136             cn : [
11137                 { 
11138                     tag: 'thead',
11139                     cn : [
11140                         { 
11141                             tag: 'tr',
11142                             cls : 'fc-first fc-last',
11143                             cn : cal_heads()
11144                         }
11145                     ]
11146                 },
11147                 { 
11148                     tag: 'tbody',
11149                     cn : cal_rows()
11150                 }
11151                   
11152             ]
11153         };
11154          
11155          var cfg = {
11156             cls : 'fc fc-ltr',
11157             cn : [
11158                 header,
11159                 {
11160                     cls : 'fc-content',
11161                     style : "position: relative;",
11162                     cn : [
11163                         {
11164                             cls : 'fc-view fc-view-month fc-grid',
11165                             style : 'position: relative',
11166                             unselectable : 'on',
11167                             cn : [
11168                                 {
11169                                     cls : 'fc-event-container',
11170                                     style : 'position:absolute;z-index:8;top:0;left:0;'
11171                                 },
11172                                 cal_table
11173                             ]
11174                         }
11175                     ]
11176     
11177                 }
11178            ] 
11179             
11180         };
11181         
11182          
11183         
11184         return cfg;
11185     },
11186     
11187     
11188     initEvents : function()
11189     {
11190         if(!this.store){
11191             throw "can not find store for calendar";
11192         }
11193         
11194         var mark = {
11195             tag: "div",
11196             cls:"x-dlg-mask",
11197             style: "text-align:center",
11198             cn: [
11199                 {
11200                     tag: "div",
11201                     style: "background-color:white;width:50%;margin:250 auto",
11202                     cn: [
11203                         {
11204                             tag: "img",
11205                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
11206                         },
11207                         {
11208                             tag: "span",
11209                             html: "Loading"
11210                         }
11211                         
11212                     ]
11213                 }
11214             ]
11215         }
11216         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
11217         
11218         var size = this.el.select('.fc-content', true).first().getSize();
11219         this.maskEl.setSize(size.width, size.height);
11220         this.maskEl.enableDisplayMode("block");
11221         if(!this.loadMask){
11222             this.maskEl.hide();
11223         }
11224         
11225         this.store = Roo.factory(this.store, Roo.data);
11226         this.store.on('load', this.onLoad, this);
11227         this.store.on('beforeload', this.onBeforeLoad, this);
11228         
11229         this.resize();
11230         
11231         this.cells = this.el.select('.fc-day',true);
11232         //Roo.log(this.cells);
11233         this.textNodes = this.el.query('.fc-day-number');
11234         this.cells.addClassOnOver('fc-state-hover');
11235         
11236         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
11237         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
11238         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
11239         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
11240         
11241         this.on('monthchange', this.onMonthChange, this);
11242         
11243         this.update(new Date().clearTime());
11244     },
11245     
11246     resize : function() {
11247         var sz  = this.el.getSize();
11248         
11249         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
11250         this.el.select('.fc-day-content div',true).setHeight(34);
11251     },
11252     
11253     
11254     // private
11255     showPrevMonth : function(e){
11256         this.update(this.activeDate.add("mo", -1));
11257     },
11258     showToday : function(e){
11259         this.update(new Date().clearTime());
11260     },
11261     // private
11262     showNextMonth : function(e){
11263         this.update(this.activeDate.add("mo", 1));
11264     },
11265
11266     // private
11267     showPrevYear : function(){
11268         this.update(this.activeDate.add("y", -1));
11269     },
11270
11271     // private
11272     showNextYear : function(){
11273         this.update(this.activeDate.add("y", 1));
11274     },
11275
11276     
11277    // private
11278     update : function(date)
11279     {
11280         var vd = this.activeDate;
11281         this.activeDate = date;
11282 //        if(vd && this.el){
11283 //            var t = date.getTime();
11284 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
11285 //                Roo.log('using add remove');
11286 //                
11287 //                this.fireEvent('monthchange', this, date);
11288 //                
11289 //                this.cells.removeClass("fc-state-highlight");
11290 //                this.cells.each(function(c){
11291 //                   if(c.dateValue == t){
11292 //                       c.addClass("fc-state-highlight");
11293 //                       setTimeout(function(){
11294 //                            try{c.dom.firstChild.focus();}catch(e){}
11295 //                       }, 50);
11296 //                       return false;
11297 //                   }
11298 //                   return true;
11299 //                });
11300 //                return;
11301 //            }
11302 //        }
11303         
11304         var days = date.getDaysInMonth();
11305         
11306         var firstOfMonth = date.getFirstDateOfMonth();
11307         var startingPos = firstOfMonth.getDay()-this.startDay;
11308         
11309         if(startingPos < this.startDay){
11310             startingPos += 7;
11311         }
11312         
11313         var pm = date.add(Date.MONTH, -1);
11314         var prevStart = pm.getDaysInMonth()-startingPos;
11315 //        
11316         this.cells = this.el.select('.fc-day',true);
11317         this.textNodes = this.el.query('.fc-day-number');
11318         this.cells.addClassOnOver('fc-state-hover');
11319         
11320         var cells = this.cells.elements;
11321         var textEls = this.textNodes;
11322         
11323         Roo.each(cells, function(cell){
11324             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
11325         });
11326         
11327         days += startingPos;
11328
11329         // convert everything to numbers so it's fast
11330         var day = 86400000;
11331         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
11332         //Roo.log(d);
11333         //Roo.log(pm);
11334         //Roo.log(prevStart);
11335         
11336         var today = new Date().clearTime().getTime();
11337         var sel = date.clearTime().getTime();
11338         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
11339         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
11340         var ddMatch = this.disabledDatesRE;
11341         var ddText = this.disabledDatesText;
11342         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
11343         var ddaysText = this.disabledDaysText;
11344         var format = this.format;
11345         
11346         var setCellClass = function(cal, cell){
11347             cell.row = 0;
11348             cell.events = [];
11349             cell.more = [];
11350             //Roo.log('set Cell Class');
11351             cell.title = "";
11352             var t = d.getTime();
11353             
11354             //Roo.log(d);
11355             
11356             cell.dateValue = t;
11357             if(t == today){
11358                 cell.className += " fc-today";
11359                 cell.className += " fc-state-highlight";
11360                 cell.title = cal.todayText;
11361             }
11362             if(t == sel){
11363                 // disable highlight in other month..
11364                 //cell.className += " fc-state-highlight";
11365                 
11366             }
11367             // disabling
11368             if(t < min) {
11369                 cell.className = " fc-state-disabled";
11370                 cell.title = cal.minText;
11371                 return;
11372             }
11373             if(t > max) {
11374                 cell.className = " fc-state-disabled";
11375                 cell.title = cal.maxText;
11376                 return;
11377             }
11378             if(ddays){
11379                 if(ddays.indexOf(d.getDay()) != -1){
11380                     cell.title = ddaysText;
11381                     cell.className = " fc-state-disabled";
11382                 }
11383             }
11384             if(ddMatch && format){
11385                 var fvalue = d.dateFormat(format);
11386                 if(ddMatch.test(fvalue)){
11387                     cell.title = ddText.replace("%0", fvalue);
11388                     cell.className = " fc-state-disabled";
11389                 }
11390             }
11391             
11392             if (!cell.initialClassName) {
11393                 cell.initialClassName = cell.dom.className;
11394             }
11395             
11396             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
11397         };
11398
11399         var i = 0;
11400         
11401         for(; i < startingPos; i++) {
11402             textEls[i].innerHTML = (++prevStart);
11403             d.setDate(d.getDate()+1);
11404             
11405             cells[i].className = "fc-past fc-other-month";
11406             setCellClass(this, cells[i]);
11407         }
11408         
11409         var intDay = 0;
11410         
11411         for(; i < days; i++){
11412             intDay = i - startingPos + 1;
11413             textEls[i].innerHTML = (intDay);
11414             d.setDate(d.getDate()+1);
11415             
11416             cells[i].className = ''; // "x-date-active";
11417             setCellClass(this, cells[i]);
11418         }
11419         var extraDays = 0;
11420         
11421         for(; i < 42; i++) {
11422             textEls[i].innerHTML = (++extraDays);
11423             d.setDate(d.getDate()+1);
11424             
11425             cells[i].className = "fc-future fc-other-month";
11426             setCellClass(this, cells[i]);
11427         }
11428         
11429         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11430         
11431         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11432         
11433         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11434         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11435         
11436         if(totalRows != 6){
11437             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11438             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11439         }
11440         
11441         this.fireEvent('monthchange', this, date);
11442         
11443         
11444         /*
11445         if(!this.internalRender){
11446             var main = this.el.dom.firstChild;
11447             var w = main.offsetWidth;
11448             this.el.setWidth(w + this.el.getBorderWidth("lr"));
11449             Roo.fly(main).setWidth(w);
11450             this.internalRender = true;
11451             // opera does not respect the auto grow header center column
11452             // then, after it gets a width opera refuses to recalculate
11453             // without a second pass
11454             if(Roo.isOpera && !this.secondPass){
11455                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11456                 this.secondPass = true;
11457                 this.update.defer(10, this, [date]);
11458             }
11459         }
11460         */
11461         
11462     },
11463     
11464     findCell : function(dt) {
11465         dt = dt.clearTime().getTime();
11466         var ret = false;
11467         this.cells.each(function(c){
11468             //Roo.log("check " +c.dateValue + '?=' + dt);
11469             if(c.dateValue == dt){
11470                 ret = c;
11471                 return false;
11472             }
11473             return true;
11474         });
11475         
11476         return ret;
11477     },
11478     
11479     findCells : function(ev) {
11480         var s = ev.start.clone().clearTime().getTime();
11481        // Roo.log(s);
11482         var e= ev.end.clone().clearTime().getTime();
11483        // Roo.log(e);
11484         var ret = [];
11485         this.cells.each(function(c){
11486              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11487             
11488             if(c.dateValue > e){
11489                 return ;
11490             }
11491             if(c.dateValue < s){
11492                 return ;
11493             }
11494             ret.push(c);
11495         });
11496         
11497         return ret;    
11498     },
11499     
11500 //    findBestRow: function(cells)
11501 //    {
11502 //        var ret = 0;
11503 //        
11504 //        for (var i =0 ; i < cells.length;i++) {
11505 //            ret  = Math.max(cells[i].rows || 0,ret);
11506 //        }
11507 //        return ret;
11508 //        
11509 //    },
11510     
11511     
11512     addItem : function(ev)
11513     {
11514         // look for vertical location slot in
11515         var cells = this.findCells(ev);
11516         
11517 //        ev.row = this.findBestRow(cells);
11518         
11519         // work out the location.
11520         
11521         var crow = false;
11522         var rows = [];
11523         for(var i =0; i < cells.length; i++) {
11524             
11525             cells[i].row = cells[0].row;
11526             
11527             if(i == 0){
11528                 cells[i].row = cells[i].row + 1;
11529             }
11530             
11531             if (!crow) {
11532                 crow = {
11533                     start : cells[i],
11534                     end :  cells[i]
11535                 };
11536                 continue;
11537             }
11538             if (crow.start.getY() == cells[i].getY()) {
11539                 // on same row.
11540                 crow.end = cells[i];
11541                 continue;
11542             }
11543             // different row.
11544             rows.push(crow);
11545             crow = {
11546                 start: cells[i],
11547                 end : cells[i]
11548             };
11549             
11550         }
11551         
11552         rows.push(crow);
11553         ev.els = [];
11554         ev.rows = rows;
11555         ev.cells = cells;
11556         
11557         cells[0].events.push(ev);
11558         
11559 //        if((typeof(cells[0].events) == 'undefined')){
11560 //            cells[0].events = [];
11561 //        }
11562 //        
11563 //        cells[0].events.push(ev);
11564 //        ev.rendered = false;
11565 //        for (var i = 0; i < cells.length;i++) {
11566 //            cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11567 //            
11568 //        }
11569         
11570         this.calevents.push(ev);
11571     },
11572     
11573     clearEvents: function() {
11574         
11575         if(!this.calevents){
11576             return;
11577         }
11578         
11579         Roo.each(this.cells.elements, function(c){
11580             c.row = 0;
11581             c.events = [];
11582             c.more = [];
11583         });
11584         
11585         Roo.each(this.calevents, function(e) {
11586             Roo.each(e.els, function(el) {
11587                 el.un('mouseenter' ,this.onEventEnter, this);
11588                 el.un('mouseleave' ,this.onEventLeave, this);
11589                 el.remove();
11590             },this);
11591         },this);
11592         
11593         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
11594             e.remove();
11595         });
11596         
11597     },
11598     
11599     renderEvents: function()
11600     {  
11601 //        for (var e = 0; e < this.calevents.length; e++) {
11602 //            
11603 //            var ev = this.calevents[e];
11604 //            var cells = ev.cells;
11605 //            var rows = ev.rows;
11606 //            
11607 //            for (var j = 0; j < cells.length; j++){
11608 //            
11609 //                if(!cells[j].more.length){
11610 //                    cells[j].row++;
11611 //                }
11612 //                if(cells[j].row > 3){
11613 //                    cells[j].more.push(ev);
11614 //                    continue;
11615 //                }
11616 //                
11617 //                cells[j].events.push(ev);
11618 //            }
11619 //        }
11620             
11621 //            for (var i = 0; i < rows.length; i++){
11622 //                // how many rows should it span..
11623 //
11624 //                var  cfg = {
11625 //                    cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11626 //                    style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11627 //
11628 //                    unselectable : "on",
11629 //                    cn : [
11630 //                        {
11631 //                            cls: 'fc-event-inner',
11632 //                            cn : [
11633 ////                                {
11634 ////                                  tag:'span',
11635 ////                                  cls: 'fc-event-time',
11636 ////                                  html : cells.length > 1 ? '' : ev.time
11637 ////                                },
11638 //                                {
11639 //                                  tag:'span',
11640 //                                  cls: 'fc-event-title',
11641 //                                  html : String.format('{0}', ev.title)
11642 //                                }
11643 //
11644 //
11645 //                            ]
11646 //                        },
11647 //                        {
11648 //                            cls: 'ui-resizable-handle ui-resizable-e',
11649 //                            html : '&nbsp;&nbsp;&nbsp'
11650 //                        }
11651 //
11652 //                    ]
11653 //                };
11654 //
11655 //                if (i == 0) {
11656 //                    cfg.cls += ' fc-event-start';
11657 //                }
11658 //                if ((i+1) == rows.length) {
11659 //                    cfg.cls += ' fc-event-end';
11660 //                }
11661 //
11662 //                var ctr = this.el.select('.fc-event-container',true).first();
11663 //                var cg = ctr.createChild(cfg);
11664 //
11665 //                var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11666 //                var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11667 //
11668 //                cg.setXY([sbox.x +2, sbox.y +(e * 20)]);    
11669 //                cg.setWidth(ebox.right - sbox.x -2);
11670 //
11671 //                cg.on('mouseenter' ,this.onEventEnter, this, ev);
11672 //                cg.on('mouseleave' ,this.onEventLeave, this, ev);
11673 //                cg.on('click', this.onEventClick, this, ev);
11674 //
11675 //                ev.els.push(cg);
11676 //
11677 //                
11678 //            }
11679 //        }
11680         
11681         var _this = this;
11682         
11683         this.cells.each(function(c) {
11684             
11685             if(c.row < 5){
11686                 return;
11687             }
11688             
11689             var ev = c.events;
11690             
11691             var r = 4;
11692             if(c.row != c.events.length){
11693                 r = 4 - (4 - (c.row - c.events.length));
11694             }
11695             
11696             c.events = ev.slice(0, r);
11697             c.more = ev.slice(r);
11698             
11699             if(c.more.length && c.more.length == 1){
11700                 c.events.push(c.more.pop());
11701             }
11702             
11703             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
11704             
11705         });
11706             
11707 //        for (var e = 0; e < this.calevents.length; e++) {
11708 //            
11709 //            var ev = this.calevents[e];
11710 //            var cells = ev.cells;
11711 //            var rows = ev.rows;
11712 //            
11713 //            for(var i = 0; i < cells.length; i++){
11714 //                
11715 //                var cbox = this.cells.item(this.cells.indexOf(cells[i]));
11716 //                
11717 //                if(cells.length < 2 && cbox.rows.length > 3){
11718 //                    cbox.more.push(ev);
11719 //                    continue;
11720 //                }
11721 //                
11722 //                cbox.rows.push(ev);
11723 //            }
11724 //        }
11725 //        
11726         this.cells.each(function(c) {
11727             
11728             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
11729             
11730             
11731             for (var e = 0; e < c.events.length; e++){
11732                 var ev = c.events[e];
11733                 var rows = ev.rows;
11734                 
11735                 for(var i = 0; i < rows.length; i++) {
11736                 
11737                     // how many rows should it span..
11738
11739                     var  cfg = {
11740                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11741                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11742
11743                         unselectable : "on",
11744                         cn : [
11745                             {
11746                                 cls: 'fc-event-inner',
11747                                 cn : [
11748     //                                {
11749     //                                  tag:'span',
11750     //                                  cls: 'fc-event-time',
11751     //                                  html : cells.length > 1 ? '' : ev.time
11752     //                                },
11753                                     {
11754                                       tag:'span',
11755                                       cls: 'fc-event-title',
11756                                       html : String.format('{0}', ev.title)
11757                                     }
11758
11759
11760                                 ]
11761                             },
11762                             {
11763                                 cls: 'ui-resizable-handle ui-resizable-e',
11764                                 html : '&nbsp;&nbsp;&nbsp'
11765                             }
11766
11767                         ]
11768                     };
11769
11770                     if (i == 0) {
11771                         cfg.cls += ' fc-event-start';
11772                     }
11773                     if ((i+1) == rows.length) {
11774                         cfg.cls += ' fc-event-end';
11775                     }
11776
11777                     var ctr = _this.el.select('.fc-event-container',true).first();
11778                     var cg = ctr.createChild(cfg);
11779
11780                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11781                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11782
11783                     var r = (c.more.length) ? 1 : 0;
11784                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
11785                     cg.setWidth(ebox.right - sbox.x -2);
11786
11787                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
11788                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
11789                     cg.on('click', _this.onEventClick, _this, ev);
11790
11791                     ev.els.push(cg);
11792                     
11793                 }
11794                 
11795             }
11796             
11797             
11798             if(c.more.length){
11799                 var  cfg = {
11800                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
11801                     style : 'position: absolute',
11802                     unselectable : "on",
11803                     cn : [
11804                         {
11805                             cls: 'fc-event-inner',
11806                             cn : [
11807                                 {
11808                                   tag:'span',
11809                                   cls: 'fc-event-title',
11810                                   html : 'More'
11811                                 }
11812
11813
11814                             ]
11815                         },
11816                         {
11817                             cls: 'ui-resizable-handle ui-resizable-e',
11818                             html : '&nbsp;&nbsp;&nbsp'
11819                         }
11820
11821                     ]
11822                 };
11823
11824                 var ctr = _this.el.select('.fc-event-container',true).first();
11825                 var cg = ctr.createChild(cfg);
11826
11827                 var sbox = c.select('.fc-day-content',true).first().getBox();
11828                 var ebox = c.select('.fc-day-content',true).first().getBox();
11829                 //Roo.log(cg);
11830                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
11831                 cg.setWidth(ebox.right - sbox.x -2);
11832
11833                 cg.on('click', _this.onMoreEventClick, _this, c.more);
11834                 
11835             }
11836             
11837         });
11838         
11839         
11840         
11841     },
11842     
11843     onEventEnter: function (e, el,event,d) {
11844         this.fireEvent('evententer', this, el, event);
11845     },
11846     
11847     onEventLeave: function (e, el,event,d) {
11848         this.fireEvent('eventleave', this, el, event);
11849     },
11850     
11851     onEventClick: function (e, el,event,d) {
11852         this.fireEvent('eventclick', this, el, event);
11853     },
11854     
11855     onMonthChange: function () {
11856         this.store.load();
11857     },
11858     
11859     onMoreEventClick: function(e, el, more)
11860     {
11861         var _this = this;
11862         
11863         this.calpopover.placement = 'right';
11864         this.calpopover.setTitle('More');
11865         
11866         this.calpopover.setContent('');
11867         
11868         var ctr = this.calpopover.el.select('.popover-content', true).first();
11869         
11870         Roo.each(more, function(m){
11871             var cfg = {
11872                 cls : 'fc-event-hori fc-event-draggable',
11873                 html : m.title
11874             }
11875             var cg = ctr.createChild(cfg);
11876             
11877             cg.on('click', _this.onEventClick, _this, m);
11878         });
11879         
11880         this.calpopover.show(el);
11881         
11882         
11883     },
11884     
11885     onLoad: function () 
11886     {   
11887         this.calevents = [];
11888         var cal = this;
11889         
11890         if(this.store.getCount() > 0){
11891             this.store.data.each(function(d){
11892                cal.addItem({
11893                     id : d.data.id,
11894                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11895                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11896                     time : d.data.start_time,
11897                     title : d.data.title,
11898                     description : d.data.description,
11899                     venue : d.data.venue
11900                 });
11901             });
11902         }
11903         
11904         this.renderEvents();
11905         
11906         if(this.calevents.length && this.loadMask){
11907             this.maskEl.hide();
11908         }
11909     },
11910     
11911     onBeforeLoad: function()
11912     {
11913         this.clearEvents();
11914         if(this.loadMask){
11915             this.maskEl.show();
11916         }
11917     }
11918 });
11919
11920  
11921  /*
11922  * - LGPL
11923  *
11924  * element
11925  * 
11926  */
11927
11928 /**
11929  * @class Roo.bootstrap.Popover
11930  * @extends Roo.bootstrap.Component
11931  * Bootstrap Popover class
11932  * @cfg {String} html contents of the popover   (or false to use children..)
11933  * @cfg {String} title of popover (or false to hide)
11934  * @cfg {String} placement how it is placed
11935  * @cfg {String} trigger click || hover (or false to trigger manually)
11936  * @cfg {String} over what (parent or false to trigger manually.)
11937  * 
11938  * @constructor
11939  * Create a new Popover
11940  * @param {Object} config The config object
11941  */
11942
11943 Roo.bootstrap.Popover = function(config){
11944     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11945 };
11946
11947 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
11948     
11949     title: 'Fill in a title',
11950     html: false,
11951     
11952     placement : 'right',
11953     trigger : 'hover', // hover
11954     
11955     over: 'parent',
11956     
11957     can_build_overlaid : false,
11958     
11959     getChildContainer : function()
11960     {
11961         return this.el.select('.popover-content',true).first();
11962     },
11963     
11964     getAutoCreate : function(){
11965          Roo.log('make popover?');
11966         var cfg = {
11967            cls : 'popover roo-dynamic',
11968            style: 'display:block',
11969            cn : [
11970                 {
11971                     cls : 'arrow'
11972                 },
11973                 {
11974                     cls : 'popover-inner',
11975                     cn : [
11976                         {
11977                             tag: 'h3',
11978                             cls: 'popover-title',
11979                             html : this.title
11980                         },
11981                         {
11982                             cls : 'popover-content',
11983                             html : this.html
11984                         }
11985                     ]
11986                     
11987                 }
11988            ]
11989         };
11990         
11991         return cfg;
11992     },
11993     setTitle: function(str)
11994     {
11995         this.el.select('.popover-title',true).first().dom.innerHTML = str;
11996     },
11997     setContent: function(str)
11998     {
11999         this.el.select('.popover-content',true).first().dom.innerHTML = str;
12000     },
12001     // as it get's added to the bottom of the page.
12002     onRender : function(ct, position)
12003     {
12004         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
12005         if(!this.el){
12006             var cfg = Roo.apply({},  this.getAutoCreate());
12007             cfg.id = Roo.id();
12008             
12009             if (this.cls) {
12010                 cfg.cls += ' ' + this.cls;
12011             }
12012             if (this.style) {
12013                 cfg.style = this.style;
12014             }
12015             Roo.log("adding to ")
12016             this.el = Roo.get(document.body).createChild(cfg, position);
12017             Roo.log(this.el);
12018         }
12019         this.initEvents();
12020     },
12021     
12022     initEvents : function()
12023     {
12024         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
12025         this.el.enableDisplayMode('block');
12026         this.el.hide();
12027         if (this.over === false) {
12028             return; 
12029         }
12030         if (this.triggers === false) {
12031             return;
12032         }
12033         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12034         var triggers = this.trigger ? this.trigger.split(' ') : [];
12035         Roo.each(triggers, function(trigger) {
12036         
12037             if (trigger == 'click') {
12038                 on_el.on('click', this.toggle, this);
12039             } else if (trigger != 'manual') {
12040                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
12041                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
12042       
12043                 on_el.on(eventIn  ,this.enter, this);
12044                 on_el.on(eventOut, this.leave, this);
12045             }
12046         }, this);
12047         
12048     },
12049     
12050     
12051     // private
12052     timeout : null,
12053     hoverState : null,
12054     
12055     toggle : function () {
12056         this.hoverState == 'in' ? this.leave() : this.enter();
12057     },
12058     
12059     enter : function () {
12060        
12061     
12062         clearTimeout(this.timeout);
12063     
12064         this.hoverState = 'in'
12065     
12066         if (!this.delay || !this.delay.show) {
12067             this.show();
12068             return 
12069         }
12070         var _t = this;
12071         this.timeout = setTimeout(function () {
12072             if (_t.hoverState == 'in') {
12073                 _t.show();
12074             }
12075         }, this.delay.show)
12076     },
12077     leave : function() {
12078         clearTimeout(this.timeout);
12079     
12080         this.hoverState = 'out'
12081     
12082         if (!this.delay || !this.delay.hide) {
12083             this.hide();
12084             return 
12085         }
12086         var _t = this;
12087         this.timeout = setTimeout(function () {
12088             if (_t.hoverState == 'out') {
12089                 _t.hide();
12090             }
12091         }, this.delay.hide)
12092     },
12093     
12094     show : function (on_el)
12095     {
12096         if (!on_el) {
12097             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12098         }
12099         // set content.
12100         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
12101         if (this.html !== false) {
12102             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
12103         }
12104         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
12105         if (!this.title.length) {
12106             this.el.select('.popover-title',true).hide();
12107         }
12108         
12109         var placement = typeof this.placement == 'function' ?
12110             this.placement.call(this, this.el, on_el) :
12111             this.placement;
12112             
12113         var autoToken = /\s?auto?\s?/i;
12114         var autoPlace = autoToken.test(placement);
12115         if (autoPlace) {
12116             placement = placement.replace(autoToken, '') || 'top';
12117         }
12118         
12119         //this.el.detach()
12120         //this.el.setXY([0,0]);
12121         this.el.show();
12122         this.el.dom.style.display='block';
12123         this.el.addClass(placement);
12124         
12125         //this.el.appendTo(on_el);
12126         
12127         var p = this.getPosition();
12128         var box = this.el.getBox();
12129         
12130         if (autoPlace) {
12131             // fixme..
12132         }
12133         var align = Roo.bootstrap.Popover.alignment[placement]
12134         this.el.alignTo(on_el, align[0],align[1]);
12135         //var arrow = this.el.select('.arrow',true).first();
12136         //arrow.set(align[2], 
12137         
12138         this.el.addClass('in');
12139         this.hoverState = null;
12140         
12141         if (this.el.hasClass('fade')) {
12142             // fade it?
12143         }
12144         
12145     },
12146     hide : function()
12147     {
12148         this.el.setXY([0,0]);
12149         this.el.removeClass('in');
12150         this.el.hide();
12151         
12152     }
12153     
12154 });
12155
12156 Roo.bootstrap.Popover.alignment = {
12157     'left' : ['r-l', [-10,0], 'right'],
12158     'right' : ['l-r', [10,0], 'left'],
12159     'bottom' : ['t-b', [0,10], 'top'],
12160     'top' : [ 'b-t', [0,-10], 'bottom']
12161 };
12162
12163  /*
12164  * - LGPL
12165  *
12166  * Progress
12167  * 
12168  */
12169
12170 /**
12171  * @class Roo.bootstrap.Progress
12172  * @extends Roo.bootstrap.Component
12173  * Bootstrap Progress class
12174  * @cfg {Boolean} striped striped of the progress bar
12175  * @cfg {Boolean} active animated of the progress bar
12176  * 
12177  * 
12178  * @constructor
12179  * Create a new Progress
12180  * @param {Object} config The config object
12181  */
12182
12183 Roo.bootstrap.Progress = function(config){
12184     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
12185 };
12186
12187 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
12188     
12189     striped : false,
12190     active: false,
12191     
12192     getAutoCreate : function(){
12193         var cfg = {
12194             tag: 'div',
12195             cls: 'progress'
12196         };
12197         
12198         
12199         if(this.striped){
12200             cfg.cls += ' progress-striped';
12201         }
12202       
12203         if(this.active){
12204             cfg.cls += ' active';
12205         }
12206         
12207         
12208         return cfg;
12209     }
12210    
12211 });
12212
12213  
12214
12215  /*
12216  * - LGPL
12217  *
12218  * ProgressBar
12219  * 
12220  */
12221
12222 /**
12223  * @class Roo.bootstrap.ProgressBar
12224  * @extends Roo.bootstrap.Component
12225  * Bootstrap ProgressBar class
12226  * @cfg {Number} aria_valuenow aria-value now
12227  * @cfg {Number} aria_valuemin aria-value min
12228  * @cfg {Number} aria_valuemax aria-value max
12229  * @cfg {String} label label for the progress bar
12230  * @cfg {String} panel (success | info | warning | danger )
12231  * @cfg {String} role role of the progress bar
12232  * @cfg {String} sr_only text
12233  * 
12234  * 
12235  * @constructor
12236  * Create a new ProgressBar
12237  * @param {Object} config The config object
12238  */
12239
12240 Roo.bootstrap.ProgressBar = function(config){
12241     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
12242 };
12243
12244 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
12245     
12246     aria_valuenow : 0,
12247     aria_valuemin : 0,
12248     aria_valuemax : 100,
12249     label : false,
12250     panel : false,
12251     role : false,
12252     sr_only: false,
12253     
12254     getAutoCreate : function()
12255     {
12256         
12257         var cfg = {
12258             tag: 'div',
12259             cls: 'progress-bar',
12260             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
12261         };
12262         
12263         if(this.sr_only){
12264             cfg.cn = {
12265                 tag: 'span',
12266                 cls: 'sr-only',
12267                 html: this.sr_only
12268             }
12269         }
12270         
12271         if(this.role){
12272             cfg.role = this.role;
12273         }
12274         
12275         if(this.aria_valuenow){
12276             cfg['aria-valuenow'] = this.aria_valuenow;
12277         }
12278         
12279         if(this.aria_valuemin){
12280             cfg['aria-valuemin'] = this.aria_valuemin;
12281         }
12282         
12283         if(this.aria_valuemax){
12284             cfg['aria-valuemax'] = this.aria_valuemax;
12285         }
12286         
12287         if(this.label && !this.sr_only){
12288             cfg.html = this.label;
12289         }
12290         
12291         if(this.panel){
12292             cfg.cls += ' progress-bar-' + this.panel;
12293         }
12294         
12295         return cfg;
12296     },
12297     
12298     update : function(aria_valuenow)
12299     {
12300         this.aria_valuenow = aria_valuenow;
12301         
12302         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
12303     }
12304    
12305 });
12306
12307  
12308
12309  /*
12310  * - LGPL
12311  *
12312  * TabPanel
12313  * 
12314  */
12315
12316 /**
12317  * @class Roo.bootstrap.TabPanel
12318  * @extends Roo.bootstrap.Component
12319  * Bootstrap TabPanel class
12320  * @cfg {Boolean} active panel active
12321  * @cfg {String} html panel content
12322  * @cfg {String} tabId tab relate id
12323  * @cfg {String} navId The navbar which triggers show hide
12324  * 
12325  * 
12326  * @constructor
12327  * Create a new TabPanel
12328  * @param {Object} config The config object
12329  */
12330
12331 Roo.bootstrap.TabPanel = function(config){
12332     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
12333      this.addEvents({
12334         /**
12335              * @event changed
12336              * Fires when the active status changes
12337              * @param {Roo.bootstrap.TabPanel} this
12338              * @param {Boolean} state the new state
12339             
12340          */
12341         'changed': true
12342      });
12343 };
12344
12345 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
12346     
12347     active: false,
12348     html: false,
12349     tabId: false,
12350     navId : false,
12351     
12352     getAutoCreate : function(){
12353         var cfg = {
12354             tag: 'div',
12355             cls: 'tab-pane',
12356             html: this.html || ''
12357         };
12358         
12359         if(this.active){
12360             cfg.cls += ' active';
12361         }
12362         
12363         if(this.tabId){
12364             cfg.tabId = this.tabId;
12365         }
12366         
12367         return cfg;
12368     },
12369     onRender : function(ct, position)
12370     {
12371        // Roo.log("Call onRender: " + this.xtype);
12372         
12373         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
12374         
12375         if (this.navId && this.tabId) {
12376             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
12377             if (!item) {
12378                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
12379             } else {
12380                 item.on('changed', function(item, state) {
12381                     this.setActive(state);
12382                 }, this);
12383             }
12384         }
12385         
12386     },
12387     setActive: function(state)
12388     {
12389         Roo.log("panel - set active " + this.tabId + "=" + state);
12390         
12391         this.active = state;
12392         if (!state) {
12393             this.el.removeClass('active');
12394             
12395         } else  if (!this.el.hasClass('active')) {
12396             this.el.addClass('active');
12397         }
12398         this.fireEvent('changed', this, state);
12399     }
12400     
12401     
12402 });
12403  
12404
12405  
12406
12407  /*
12408  * - LGPL
12409  *
12410  * DateField
12411  * 
12412  */
12413
12414 /**
12415  * @class Roo.bootstrap.DateField
12416  * @extends Roo.bootstrap.Input
12417  * Bootstrap DateField class
12418  * @cfg {Number} weekStart default 0
12419  * @cfg {Number} weekStart default 0
12420  * @cfg {Number} viewMode default empty, (months|years)
12421  * @cfg {Number} minViewMode default empty, (months|years)
12422  * @cfg {Number} startDate default -Infinity
12423  * @cfg {Number} endDate default Infinity
12424  * @cfg {Boolean} todayHighlight default false
12425  * @cfg {Boolean} todayBtn default false
12426  * @cfg {Boolean} calendarWeeks default false
12427  * @cfg {Object} daysOfWeekDisabled default empty
12428  * 
12429  * @cfg {Boolean} keyboardNavigation default true
12430  * @cfg {String} language default en
12431  * 
12432  * @constructor
12433  * Create a new DateField
12434  * @param {Object} config The config object
12435  */
12436
12437 Roo.bootstrap.DateField = function(config){
12438     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
12439      this.addEvents({
12440             /**
12441              * @event show
12442              * Fires when this field show.
12443              * @param {Roo.bootstrap.DateField} this
12444              * @param {Mixed} date The date value
12445              */
12446             show : true,
12447             /**
12448              * @event show
12449              * Fires when this field hide.
12450              * @param {Roo.bootstrap.DateField} this
12451              * @param {Mixed} date The date value
12452              */
12453             hide : true,
12454             /**
12455              * @event select
12456              * Fires when select a date.
12457              * @param {Roo.bootstrap.DateField} this
12458              * @param {Mixed} date The date value
12459              */
12460             select : true
12461         });
12462 };
12463
12464 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
12465     
12466     /**
12467      * @cfg {String} format
12468      * The default date format string which can be overriden for localization support.  The format must be
12469      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
12470      */
12471     format : "m/d/y",
12472     /**
12473      * @cfg {String} altFormats
12474      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
12475      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
12476      */
12477     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
12478     
12479     weekStart : 0,
12480     
12481     viewMode : '',
12482     
12483     minViewMode : '',
12484     
12485     todayHighlight : false,
12486     
12487     todayBtn: false,
12488     
12489     language: 'en',
12490     
12491     keyboardNavigation: true,
12492     
12493     calendarWeeks: false,
12494     
12495     startDate: -Infinity,
12496     
12497     endDate: Infinity,
12498     
12499     daysOfWeekDisabled: [],
12500     
12501     _events: [],
12502     
12503     UTCDate: function()
12504     {
12505         return new Date(Date.UTC.apply(Date, arguments));
12506     },
12507     
12508     UTCToday: function()
12509     {
12510         var today = new Date();
12511         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
12512     },
12513     
12514     getDate: function() {
12515             var d = this.getUTCDate();
12516             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
12517     },
12518     
12519     getUTCDate: function() {
12520             return this.date;
12521     },
12522     
12523     setDate: function(d) {
12524             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
12525     },
12526     
12527     setUTCDate: function(d) {
12528             this.date = d;
12529             this.setValue(this.formatDate(this.date));
12530     },
12531         
12532     onRender: function(ct, position)
12533     {
12534         
12535         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
12536         
12537         this.language = this.language || 'en';
12538         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
12539         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
12540         
12541         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
12542         this.format = this.format || 'm/d/y';
12543         this.isInline = false;
12544         this.isInput = true;
12545         this.component = this.el.select('.add-on', true).first() || false;
12546         this.component = (this.component && this.component.length === 0) ? false : this.component;
12547         this.hasInput = this.component && this.inputEL().length;
12548         
12549         if (typeof(this.minViewMode === 'string')) {
12550             switch (this.minViewMode) {
12551                 case 'months':
12552                     this.minViewMode = 1;
12553                     break;
12554                 case 'years':
12555                     this.minViewMode = 2;
12556                     break;
12557                 default:
12558                     this.minViewMode = 0;
12559                     break;
12560             }
12561         }
12562         
12563         if (typeof(this.viewMode === 'string')) {
12564             switch (this.viewMode) {
12565                 case 'months':
12566                     this.viewMode = 1;
12567                     break;
12568                 case 'years':
12569                     this.viewMode = 2;
12570                     break;
12571                 default:
12572                     this.viewMode = 0;
12573                     break;
12574             }
12575         }
12576                 
12577         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
12578         
12579         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12580         
12581         this.picker().on('mousedown', this.onMousedown, this);
12582         this.picker().on('click', this.onClick, this);
12583         
12584         this.picker().addClass('datepicker-dropdown');
12585         
12586         this.startViewMode = this.viewMode;
12587         
12588         
12589         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
12590             if(!this.calendarWeeks){
12591                 v.remove();
12592                 return;
12593             };
12594             
12595             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
12596             v.attr('colspan', function(i, val){
12597                 return parseInt(val) + 1;
12598             });
12599         })
12600                         
12601         
12602         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
12603         
12604         this.setStartDate(this.startDate);
12605         this.setEndDate(this.endDate);
12606         
12607         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
12608         
12609         this.fillDow();
12610         this.fillMonths();
12611         this.update();
12612         this.showMode();
12613         
12614         if(this.isInline) {
12615             this.show();
12616         }
12617     },
12618     
12619     picker : function()
12620     {
12621         return this.el.select('.datepicker', true).first();
12622     },
12623     
12624     fillDow: function()
12625     {
12626         var dowCnt = this.weekStart;
12627         
12628         var dow = {
12629             tag: 'tr',
12630             cn: [
12631                 
12632             ]
12633         };
12634         
12635         if(this.calendarWeeks){
12636             dow.cn.push({
12637                 tag: 'th',
12638                 cls: 'cw',
12639                 html: '&nbsp;'
12640             })
12641         }
12642         
12643         while (dowCnt < this.weekStart + 7) {
12644             dow.cn.push({
12645                 tag: 'th',
12646                 cls: 'dow',
12647                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12648             });
12649         }
12650         
12651         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12652     },
12653     
12654     fillMonths: function()
12655     {    
12656         var i = 0
12657         var months = this.picker().select('>.datepicker-months td', true).first();
12658         
12659         months.dom.innerHTML = '';
12660         
12661         while (i < 12) {
12662             var month = {
12663                 tag: 'span',
12664                 cls: 'month',
12665                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12666             }
12667             
12668             months.createChild(month);
12669         }
12670         
12671     },
12672     
12673     update: function(){
12674         
12675         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12676         
12677         if (this.date < this.startDate) {
12678             this.viewDate = new Date(this.startDate);
12679         } else if (this.date > this.endDate) {
12680             this.viewDate = new Date(this.endDate);
12681         } else {
12682             this.viewDate = new Date(this.date);
12683         }
12684         
12685         this.fill();
12686     },
12687     
12688     fill: function() {
12689         var d = new Date(this.viewDate),
12690                 year = d.getUTCFullYear(),
12691                 month = d.getUTCMonth(),
12692                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12693                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12694                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12695                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12696                 currentDate = this.date && this.date.valueOf(),
12697                 today = this.UTCToday();
12698         
12699         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12700         
12701 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12702         
12703 //        this.picker.select('>tfoot th.today').
12704 //                                              .text(dates[this.language].today)
12705 //                                              .toggle(this.todayBtn !== false);
12706     
12707         this.updateNavArrows();
12708         this.fillMonths();
12709                                                 
12710         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12711         
12712         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12713          
12714         prevMonth.setUTCDate(day);
12715         
12716         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12717         
12718         var nextMonth = new Date(prevMonth);
12719         
12720         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12721         
12722         nextMonth = nextMonth.valueOf();
12723         
12724         var fillMonths = false;
12725         
12726         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12727         
12728         while(prevMonth.valueOf() < nextMonth) {
12729             var clsName = '';
12730             
12731             if (prevMonth.getUTCDay() === this.weekStart) {
12732                 if(fillMonths){
12733                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12734                 }
12735                     
12736                 fillMonths = {
12737                     tag: 'tr',
12738                     cn: []
12739                 };
12740                 
12741                 if(this.calendarWeeks){
12742                     // ISO 8601: First week contains first thursday.
12743                     // ISO also states week starts on Monday, but we can be more abstract here.
12744                     var
12745                     // Start of current week: based on weekstart/current date
12746                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12747                     // Thursday of this week
12748                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12749                     // First Thursday of year, year from thursday
12750                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12751                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12752                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12753                     
12754                     fillMonths.cn.push({
12755                         tag: 'td',
12756                         cls: 'cw',
12757                         html: calWeek
12758                     });
12759                 }
12760             }
12761             
12762             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12763                 clsName += ' old';
12764             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12765                 clsName += ' new';
12766             }
12767             if (this.todayHighlight &&
12768                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12769                 prevMonth.getUTCMonth() == today.getMonth() &&
12770                 prevMonth.getUTCDate() == today.getDate()) {
12771                 clsName += ' today';
12772             }
12773             
12774             if (currentDate && prevMonth.valueOf() === currentDate) {
12775                 clsName += ' active';
12776             }
12777             
12778             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12779                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12780                     clsName += ' disabled';
12781             }
12782             
12783             fillMonths.cn.push({
12784                 tag: 'td',
12785                 cls: 'day ' + clsName,
12786                 html: prevMonth.getDate()
12787             })
12788             
12789             prevMonth.setDate(prevMonth.getDate()+1);
12790         }
12791           
12792         var currentYear = this.date && this.date.getUTCFullYear();
12793         var currentMonth = this.date && this.date.getUTCMonth();
12794         
12795         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12796         
12797         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12798             v.removeClass('active');
12799             
12800             if(currentYear === year && k === currentMonth){
12801                 v.addClass('active');
12802             }
12803             
12804             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12805                 v.addClass('disabled');
12806             }
12807             
12808         });
12809         
12810         
12811         year = parseInt(year/10, 10) * 10;
12812         
12813         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12814         
12815         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12816         
12817         year -= 1;
12818         for (var i = -1; i < 11; i++) {
12819             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12820                 tag: 'span',
12821                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12822                 html: year
12823             })
12824             
12825             year += 1;
12826         }
12827     },
12828     
12829     showMode: function(dir) {
12830         if (dir) {
12831             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12832         }
12833         Roo.each(this.picker().select('>div',true).elements, function(v){
12834             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12835             v.hide();
12836         });
12837         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12838     },
12839     
12840     place: function()
12841     {
12842         if(this.isInline) return;
12843         
12844         this.picker().removeClass(['bottom', 'top']);
12845         
12846         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12847             /*
12848              * place to the top of element!
12849              *
12850              */
12851             
12852             this.picker().addClass('top');
12853             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12854             
12855             return;
12856         }
12857         
12858         this.picker().addClass('bottom');
12859         
12860         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12861     },
12862     
12863     parseDate : function(value){
12864         if(!value || value instanceof Date){
12865             return value;
12866         }
12867         var v = Date.parseDate(value, this.format);
12868         if (!v && this.useIso) {
12869             v = Date.parseDate(value, 'Y-m-d');
12870         }
12871         if(!v && this.altFormats){
12872             if(!this.altFormatsArray){
12873                 this.altFormatsArray = this.altFormats.split("|");
12874             }
12875             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12876                 v = Date.parseDate(value, this.altFormatsArray[i]);
12877             }
12878         }
12879         return v;
12880     },
12881     
12882     formatDate : function(date, fmt){
12883         return (!date || !(date instanceof Date)) ?
12884         date : date.dateFormat(fmt || this.format);
12885     },
12886     
12887     onFocus : function()
12888     {
12889         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12890         this.show();
12891     },
12892     
12893     onBlur : function()
12894     {
12895         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12896         this.hide();
12897     },
12898     
12899     show : function()
12900     {
12901         this.picker().show();
12902         this.update();
12903         this.place();
12904         
12905         this.fireEvent('show', this, this.date);
12906     },
12907     
12908     hide : function()
12909     {
12910         if(this.isInline) return;
12911         this.picker().hide();
12912         this.viewMode = this.startViewMode;
12913         this.showMode();
12914         
12915         this.fireEvent('hide', this, this.date);
12916         
12917     },
12918     
12919     onMousedown: function(e){
12920         e.stopPropagation();
12921         e.preventDefault();
12922     },
12923     
12924     keyup: function(e){
12925         Roo.bootstrap.DateField.superclass.keyup.call(this);
12926         this.update();
12927         
12928     },
12929
12930     setValue: function(v){
12931         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12932         
12933         this.fireEvent('select', this, this.date);
12934         
12935     },
12936     
12937     fireKey: function(e){
12938         if (!this.picker().isVisible()){
12939             if (e.keyCode == 27) // allow escape to hide and re-show picker
12940                 this.show();
12941             return;
12942         }
12943         var dateChanged = false,
12944         dir, day, month,
12945         newDate, newViewDate;
12946         switch(e.keyCode){
12947             case 27: // escape
12948                 this.hide();
12949                 e.preventDefault();
12950                 break;
12951             case 37: // left
12952             case 39: // right
12953                 if (!this.keyboardNavigation) break;
12954                 dir = e.keyCode == 37 ? -1 : 1;
12955                 
12956                 if (e.ctrlKey){
12957                     newDate = this.moveYear(this.date, dir);
12958                     newViewDate = this.moveYear(this.viewDate, dir);
12959                 } else if (e.shiftKey){
12960                     newDate = this.moveMonth(this.date, dir);
12961                     newViewDate = this.moveMonth(this.viewDate, dir);
12962                 } else {
12963                     newDate = new Date(this.date);
12964                     newDate.setUTCDate(this.date.getUTCDate() + dir);
12965                     newViewDate = new Date(this.viewDate);
12966                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12967                 }
12968                 if (this.dateWithinRange(newDate)){
12969                     this.date = newDate;
12970                     this.viewDate = newViewDate;
12971                     this.setValue(this.formatDate(this.date));
12972                     this.update();
12973                     e.preventDefault();
12974                     dateChanged = true;
12975                 }
12976                 break;
12977             case 38: // up
12978             case 40: // down
12979                 if (!this.keyboardNavigation) break;
12980                 dir = e.keyCode == 38 ? -1 : 1;
12981                 if (e.ctrlKey){
12982                     newDate = this.moveYear(this.date, dir);
12983                     newViewDate = this.moveYear(this.viewDate, dir);
12984                 } else if (e.shiftKey){
12985                     newDate = this.moveMonth(this.date, dir);
12986                     newViewDate = this.moveMonth(this.viewDate, dir);
12987                 } else {
12988                     newDate = new Date(this.date);
12989                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12990                     newViewDate = new Date(this.viewDate);
12991                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12992                 }
12993                 if (this.dateWithinRange(newDate)){
12994                     this.date = newDate;
12995                     this.viewDate = newViewDate;
12996                     this.setValue(this.formatDate(this.date));
12997                     this.update();
12998                     e.preventDefault();
12999                     dateChanged = true;
13000                 }
13001                 break;
13002             case 13: // enter
13003                 this.setValue(this.formatDate(this.date));
13004                 this.hide();
13005                 e.preventDefault();
13006                 break;
13007             case 9: // tab
13008                 this.setValue(this.formatDate(this.date));
13009                 this.hide();
13010                 break;
13011         }
13012     },
13013     
13014     
13015     onClick: function(e) {
13016         e.stopPropagation();
13017         e.preventDefault();
13018         
13019         var target = e.getTarget();
13020         
13021         if(target.nodeName.toLowerCase() === 'i'){
13022             target = Roo.get(target).dom.parentNode;
13023         }
13024         
13025         var nodeName = target.nodeName;
13026         var className = target.className;
13027         var html = target.innerHTML;
13028         
13029         switch(nodeName.toLowerCase()) {
13030             case 'th':
13031                 switch(className) {
13032                     case 'switch':
13033                         this.showMode(1);
13034                         break;
13035                     case 'prev':
13036                     case 'next':
13037                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
13038                         switch(this.viewMode){
13039                                 case 0:
13040                                         this.viewDate = this.moveMonth(this.viewDate, dir);
13041                                         break;
13042                                 case 1:
13043                                 case 2:
13044                                         this.viewDate = this.moveYear(this.viewDate, dir);
13045                                         break;
13046                         }
13047                         this.fill();
13048                         break;
13049                     case 'today':
13050                         var date = new Date();
13051                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
13052                         this.fill()
13053                         this.setValue(this.formatDate(this.date));
13054                         this.hide();
13055                         break;
13056                 }
13057                 break;
13058             case 'span':
13059                 if (className.indexOf('disabled') === -1) {
13060                     this.viewDate.setUTCDate(1);
13061                     if (className.indexOf('month') !== -1) {
13062                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
13063                     } else {
13064                         var year = parseInt(html, 10) || 0;
13065                         this.viewDate.setUTCFullYear(year);
13066                         
13067                     }
13068                     this.showMode(-1);
13069                     this.fill();
13070                 }
13071                 break;
13072                 
13073             case 'td':
13074                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
13075                     var day = parseInt(html, 10) || 1;
13076                     var year = this.viewDate.getUTCFullYear(),
13077                         month = this.viewDate.getUTCMonth();
13078
13079                     if (className.indexOf('old') !== -1) {
13080                         if(month === 0 ){
13081                             month = 11;
13082                             year -= 1;
13083                         }else{
13084                             month -= 1;
13085                         }
13086                     } else if (className.indexOf('new') !== -1) {
13087                         if (month == 11) {
13088                             month = 0;
13089                             year += 1;
13090                         } else {
13091                             month += 1;
13092                         }
13093                     }
13094                     this.date = this.UTCDate(year, month, day,0,0,0,0);
13095                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
13096                     this.fill();
13097                     this.setValue(this.formatDate(this.date));
13098                     this.hide();
13099                 }
13100                 break;
13101         }
13102     },
13103     
13104     setStartDate: function(startDate){
13105         this.startDate = startDate || -Infinity;
13106         if (this.startDate !== -Infinity) {
13107             this.startDate = this.parseDate(this.startDate);
13108         }
13109         this.update();
13110         this.updateNavArrows();
13111     },
13112
13113     setEndDate: function(endDate){
13114         this.endDate = endDate || Infinity;
13115         if (this.endDate !== Infinity) {
13116             this.endDate = this.parseDate(this.endDate);
13117         }
13118         this.update();
13119         this.updateNavArrows();
13120     },
13121     
13122     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
13123         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
13124         if (typeof(this.daysOfWeekDisabled) !== 'object') {
13125             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
13126         }
13127         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
13128             return parseInt(d, 10);
13129         });
13130         this.update();
13131         this.updateNavArrows();
13132     },
13133     
13134     updateNavArrows: function() {
13135         var d = new Date(this.viewDate),
13136         year = d.getUTCFullYear(),
13137         month = d.getUTCMonth();
13138         
13139         Roo.each(this.picker().select('.prev', true).elements, function(v){
13140             v.show();
13141             switch (this.viewMode) {
13142                 case 0:
13143
13144                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
13145                         v.hide();
13146                     }
13147                     break;
13148                 case 1:
13149                 case 2:
13150                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
13151                         v.hide();
13152                     }
13153                     break;
13154             }
13155         });
13156         
13157         Roo.each(this.picker().select('.next', true).elements, function(v){
13158             v.show();
13159             switch (this.viewMode) {
13160                 case 0:
13161
13162                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
13163                         v.hide();
13164                     }
13165                     break;
13166                 case 1:
13167                 case 2:
13168                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
13169                         v.hide();
13170                     }
13171                     break;
13172             }
13173         })
13174     },
13175     
13176     moveMonth: function(date, dir){
13177         if (!dir) return date;
13178         var new_date = new Date(date.valueOf()),
13179         day = new_date.getUTCDate(),
13180         month = new_date.getUTCMonth(),
13181         mag = Math.abs(dir),
13182         new_month, test;
13183         dir = dir > 0 ? 1 : -1;
13184         if (mag == 1){
13185             test = dir == -1
13186             // If going back one month, make sure month is not current month
13187             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
13188             ? function(){
13189                 return new_date.getUTCMonth() == month;
13190             }
13191             // If going forward one month, make sure month is as expected
13192             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
13193             : function(){
13194                 return new_date.getUTCMonth() != new_month;
13195             };
13196             new_month = month + dir;
13197             new_date.setUTCMonth(new_month);
13198             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
13199             if (new_month < 0 || new_month > 11)
13200                 new_month = (new_month + 12) % 12;
13201         } else {
13202             // For magnitudes >1, move one month at a time...
13203             for (var i=0; i<mag; i++)
13204                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
13205                 new_date = this.moveMonth(new_date, dir);
13206             // ...then reset the day, keeping it in the new month
13207             new_month = new_date.getUTCMonth();
13208             new_date.setUTCDate(day);
13209             test = function(){
13210                 return new_month != new_date.getUTCMonth();
13211             };
13212         }
13213         // Common date-resetting loop -- if date is beyond end of month, make it
13214         // end of month
13215         while (test()){
13216             new_date.setUTCDate(--day);
13217             new_date.setUTCMonth(new_month);
13218         }
13219         return new_date;
13220     },
13221
13222     moveYear: function(date, dir){
13223         return this.moveMonth(date, dir*12);
13224     },
13225
13226     dateWithinRange: function(date){
13227         return date >= this.startDate && date <= this.endDate;
13228     },
13229
13230     
13231     remove: function() {
13232         this.picker().remove();
13233     }
13234    
13235 });
13236
13237 Roo.apply(Roo.bootstrap.DateField,  {
13238     
13239     head : {
13240         tag: 'thead',
13241         cn: [
13242         {
13243             tag: 'tr',
13244             cn: [
13245             {
13246                 tag: 'th',
13247                 cls: 'prev',
13248                 html: '<i class="icon-arrow-left"/>'
13249             },
13250             {
13251                 tag: 'th',
13252                 cls: 'switch',
13253                 colspan: '5'
13254             },
13255             {
13256                 tag: 'th',
13257                 cls: 'next',
13258                 html: '<i class="icon-arrow-right"/>'
13259             }
13260
13261             ]
13262         }
13263         ]
13264     },
13265     
13266     content : {
13267         tag: 'tbody',
13268         cn: [
13269         {
13270             tag: 'tr',
13271             cn: [
13272             {
13273                 tag: 'td',
13274                 colspan: '7'
13275             }
13276             ]
13277         }
13278         ]
13279     },
13280     
13281     footer : {
13282         tag: 'tfoot',
13283         cn: [
13284         {
13285             tag: 'tr',
13286             cn: [
13287             {
13288                 tag: 'th',
13289                 colspan: '7',
13290                 cls: 'today'
13291             }
13292                     
13293             ]
13294         }
13295         ]
13296     },
13297     
13298     dates:{
13299         en: {
13300             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
13301             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
13302             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
13303             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
13304             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
13305             today: "Today"
13306         }
13307     },
13308     
13309     modes: [
13310     {
13311         clsName: 'days',
13312         navFnc: 'Month',
13313         navStep: 1
13314     },
13315     {
13316         clsName: 'months',
13317         navFnc: 'FullYear',
13318         navStep: 1
13319     },
13320     {
13321         clsName: 'years',
13322         navFnc: 'FullYear',
13323         navStep: 10
13324     }]
13325 });
13326
13327 Roo.apply(Roo.bootstrap.DateField,  {
13328   
13329     template : {
13330         tag: 'div',
13331         cls: 'datepicker dropdown-menu',
13332         cn: [
13333         {
13334             tag: 'div',
13335             cls: 'datepicker-days',
13336             cn: [
13337             {
13338                 tag: 'table',
13339                 cls: 'table-condensed',
13340                 cn:[
13341                 Roo.bootstrap.DateField.head,
13342                 {
13343                     tag: 'tbody'
13344                 },
13345                 Roo.bootstrap.DateField.footer
13346                 ]
13347             }
13348             ]
13349         },
13350         {
13351             tag: 'div',
13352             cls: 'datepicker-months',
13353             cn: [
13354             {
13355                 tag: 'table',
13356                 cls: 'table-condensed',
13357                 cn:[
13358                 Roo.bootstrap.DateField.head,
13359                 Roo.bootstrap.DateField.content,
13360                 Roo.bootstrap.DateField.footer
13361                 ]
13362             }
13363             ]
13364         },
13365         {
13366             tag: 'div',
13367             cls: 'datepicker-years',
13368             cn: [
13369             {
13370                 tag: 'table',
13371                 cls: 'table-condensed',
13372                 cn:[
13373                 Roo.bootstrap.DateField.head,
13374                 Roo.bootstrap.DateField.content,
13375                 Roo.bootstrap.DateField.footer
13376                 ]
13377             }
13378             ]
13379         }
13380         ]
13381     }
13382 });
13383
13384  
13385
13386  /*
13387  * - LGPL
13388  *
13389  * TimeField
13390  * 
13391  */
13392
13393 /**
13394  * @class Roo.bootstrap.TimeField
13395  * @extends Roo.bootstrap.Input
13396  * Bootstrap DateField class
13397  * 
13398  * 
13399  * @constructor
13400  * Create a new TimeField
13401  * @param {Object} config The config object
13402  */
13403
13404 Roo.bootstrap.TimeField = function(config){
13405     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
13406     this.addEvents({
13407             /**
13408              * @event show
13409              * Fires when this field show.
13410              * @param {Roo.bootstrap.DateField} this
13411              * @param {Mixed} date The date value
13412              */
13413             show : true,
13414             /**
13415              * @event show
13416              * Fires when this field hide.
13417              * @param {Roo.bootstrap.DateField} this
13418              * @param {Mixed} date The date value
13419              */
13420             hide : true,
13421             /**
13422              * @event select
13423              * Fires when select a date.
13424              * @param {Roo.bootstrap.DateField} this
13425              * @param {Mixed} date The date value
13426              */
13427             select : true
13428         });
13429 };
13430
13431 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
13432     
13433     /**
13434      * @cfg {String} format
13435      * The default time format string which can be overriden for localization support.  The format must be
13436      * valid according to {@link Date#parseDate} (defaults to 'H:i').
13437      */
13438     format : "H:i",
13439        
13440     onRender: function(ct, position)
13441     {
13442         
13443         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
13444                 
13445         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
13446         
13447         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13448         
13449         this.pop = this.picker().select('>.datepicker-time',true).first();
13450         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
13451         
13452         this.picker().on('mousedown', this.onMousedown, this);
13453         this.picker().on('click', this.onClick, this);
13454         
13455         this.picker().addClass('datepicker-dropdown');
13456     
13457         this.fillTime();
13458         this.update();
13459             
13460         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
13461         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
13462         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
13463         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
13464         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
13465         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
13466
13467     },
13468     
13469     fireKey: function(e){
13470         if (!this.picker().isVisible()){
13471             if (e.keyCode == 27) // allow escape to hide and re-show picker
13472                 this.show();
13473             return;
13474         }
13475
13476         e.preventDefault();
13477         
13478         switch(e.keyCode){
13479             case 27: // escape
13480                 this.hide();
13481                 break;
13482             case 37: // left
13483             case 39: // right
13484                 this.onTogglePeriod();
13485                 break;
13486             case 38: // up
13487                 this.onIncrementMinutes();
13488                 break;
13489             case 40: // down
13490                 this.onDecrementMinutes();
13491                 break;
13492             case 13: // enter
13493             case 9: // tab
13494                 this.setTime();
13495                 break;
13496         }
13497     },
13498     
13499     onClick: function(e) {
13500         e.stopPropagation();
13501         e.preventDefault();
13502     },
13503     
13504     picker : function()
13505     {
13506         return this.el.select('.datepicker', true).first();
13507     },
13508     
13509     fillTime: function()
13510     {    
13511         var time = this.pop.select('tbody', true).first();
13512         
13513         time.dom.innerHTML = '';
13514         
13515         time.createChild({
13516             tag: 'tr',
13517             cn: [
13518                 {
13519                     tag: 'td',
13520                     cn: [
13521                         {
13522                             tag: 'a',
13523                             href: '#',
13524                             cls: 'btn',
13525                             cn: [
13526                                 {
13527                                     tag: 'span',
13528                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
13529                                 }
13530                             ]
13531                         } 
13532                     ]
13533                 },
13534                 {
13535                     tag: 'td',
13536                     cls: 'separator'
13537                 },
13538                 {
13539                     tag: 'td',
13540                     cn: [
13541                         {
13542                             tag: 'a',
13543                             href: '#',
13544                             cls: 'btn',
13545                             cn: [
13546                                 {
13547                                     tag: 'span',
13548                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
13549                                 }
13550                             ]
13551                         }
13552                     ]
13553                 },
13554                 {
13555                     tag: 'td',
13556                     cls: 'separator'
13557                 }
13558             ]
13559         });
13560         
13561         time.createChild({
13562             tag: 'tr',
13563             cn: [
13564                 {
13565                     tag: 'td',
13566                     cn: [
13567                         {
13568                             tag: 'span',
13569                             cls: 'timepicker-hour',
13570                             html: '00'
13571                         }  
13572                     ]
13573                 },
13574                 {
13575                     tag: 'td',
13576                     cls: 'separator',
13577                     html: ':'
13578                 },
13579                 {
13580                     tag: 'td',
13581                     cn: [
13582                         {
13583                             tag: 'span',
13584                             cls: 'timepicker-minute',
13585                             html: '00'
13586                         }  
13587                     ]
13588                 },
13589                 {
13590                     tag: 'td',
13591                     cls: 'separator'
13592                 },
13593                 {
13594                     tag: 'td',
13595                     cn: [
13596                         {
13597                             tag: 'button',
13598                             type: 'button',
13599                             cls: 'btn btn-primary period',
13600                             html: 'AM'
13601                             
13602                         }
13603                     ]
13604                 }
13605             ]
13606         });
13607         
13608         time.createChild({
13609             tag: 'tr',
13610             cn: [
13611                 {
13612                     tag: 'td',
13613                     cn: [
13614                         {
13615                             tag: 'a',
13616                             href: '#',
13617                             cls: 'btn',
13618                             cn: [
13619                                 {
13620                                     tag: 'span',
13621                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
13622                                 }
13623                             ]
13624                         }
13625                     ]
13626                 },
13627                 {
13628                     tag: 'td',
13629                     cls: 'separator'
13630                 },
13631                 {
13632                     tag: 'td',
13633                     cn: [
13634                         {
13635                             tag: 'a',
13636                             href: '#',
13637                             cls: 'btn',
13638                             cn: [
13639                                 {
13640                                     tag: 'span',
13641                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
13642                                 }
13643                             ]
13644                         }
13645                     ]
13646                 },
13647                 {
13648                     tag: 'td',
13649                     cls: 'separator'
13650                 }
13651             ]
13652         });
13653         
13654     },
13655     
13656     update: function()
13657     {
13658         
13659         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13660         
13661         this.fill();
13662     },
13663     
13664     fill: function() 
13665     {
13666         var hours = this.time.getHours();
13667         var minutes = this.time.getMinutes();
13668         var period = 'AM';
13669         
13670         if(hours > 11){
13671             period = 'PM';
13672         }
13673         
13674         if(hours == 0){
13675             hours = 12;
13676         }
13677         
13678         
13679         if(hours > 12){
13680             hours = hours - 12;
13681         }
13682         
13683         if(hours < 10){
13684             hours = '0' + hours;
13685         }
13686         
13687         if(minutes < 10){
13688             minutes = '0' + minutes;
13689         }
13690         
13691         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13692         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13693         this.pop.select('button', true).first().dom.innerHTML = period;
13694         
13695     },
13696     
13697     place: function()
13698     {   
13699         this.picker().removeClass(['bottom', 'top']);
13700         
13701         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13702             /*
13703              * place to the top of element!
13704              *
13705              */
13706             
13707             this.picker().addClass('top');
13708             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13709             
13710             return;
13711         }
13712         
13713         this.picker().addClass('bottom');
13714         
13715         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13716     },
13717   
13718     onFocus : function()
13719     {
13720         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13721         this.show();
13722     },
13723     
13724     onBlur : function()
13725     {
13726         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13727         this.hide();
13728     },
13729     
13730     show : function()
13731     {
13732         this.picker().show();
13733         this.pop.show();
13734         this.update();
13735         this.place();
13736         
13737         this.fireEvent('show', this, this.date);
13738     },
13739     
13740     hide : function()
13741     {
13742         this.picker().hide();
13743         this.pop.hide();
13744         
13745         this.fireEvent('hide', this, this.date);
13746     },
13747     
13748     setTime : function()
13749     {
13750         this.hide();
13751         this.setValue(this.time.format(this.format));
13752         
13753         this.fireEvent('select', this, this.date);
13754         
13755         
13756     },
13757     
13758     onMousedown: function(e){
13759         e.stopPropagation();
13760         e.preventDefault();
13761     },
13762     
13763     onIncrementHours: function()
13764     {
13765         Roo.log('onIncrementHours');
13766         this.time = this.time.add(Date.HOUR, 1);
13767         this.update();
13768         
13769     },
13770     
13771     onDecrementHours: function()
13772     {
13773         Roo.log('onDecrementHours');
13774         this.time = this.time.add(Date.HOUR, -1);
13775         this.update();
13776     },
13777     
13778     onIncrementMinutes: function()
13779     {
13780         Roo.log('onIncrementMinutes');
13781         this.time = this.time.add(Date.MINUTE, 1);
13782         this.update();
13783     },
13784     
13785     onDecrementMinutes: function()
13786     {
13787         Roo.log('onDecrementMinutes');
13788         this.time = this.time.add(Date.MINUTE, -1);
13789         this.update();
13790     },
13791     
13792     onTogglePeriod: function()
13793     {
13794         Roo.log('onTogglePeriod');
13795         this.time = this.time.add(Date.HOUR, 12);
13796         this.update();
13797     }
13798     
13799    
13800 });
13801
13802 Roo.apply(Roo.bootstrap.TimeField,  {
13803     
13804     content : {
13805         tag: 'tbody',
13806         cn: [
13807             {
13808                 tag: 'tr',
13809                 cn: [
13810                 {
13811                     tag: 'td',
13812                     colspan: '7'
13813                 }
13814                 ]
13815             }
13816         ]
13817     },
13818     
13819     footer : {
13820         tag: 'tfoot',
13821         cn: [
13822             {
13823                 tag: 'tr',
13824                 cn: [
13825                 {
13826                     tag: 'th',
13827                     colspan: '7',
13828                     cls: '',
13829                     cn: [
13830                         {
13831                             tag: 'button',
13832                             cls: 'btn btn-info ok',
13833                             html: 'OK'
13834                         }
13835                     ]
13836                 }
13837
13838                 ]
13839             }
13840         ]
13841     }
13842 });
13843
13844 Roo.apply(Roo.bootstrap.TimeField,  {
13845   
13846     template : {
13847         tag: 'div',
13848         cls: 'datepicker dropdown-menu',
13849         cn: [
13850             {
13851                 tag: 'div',
13852                 cls: 'datepicker-time',
13853                 cn: [
13854                 {
13855                     tag: 'table',
13856                     cls: 'table-condensed',
13857                     cn:[
13858                     Roo.bootstrap.TimeField.content,
13859                     Roo.bootstrap.TimeField.footer
13860                     ]
13861                 }
13862                 ]
13863             }
13864         ]
13865     }
13866 });
13867
13868  
13869
13870  /*
13871  * - LGPL
13872  *
13873  * CheckBox
13874  * 
13875  */
13876
13877 /**
13878  * @class Roo.bootstrap.CheckBox
13879  * @extends Roo.bootstrap.Input
13880  * Bootstrap CheckBox class
13881  * 
13882  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13883  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13884  * @cfg {String} boxLabel The text that appears beside the checkbox
13885  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
13886  * @cfg {Boolean} checked initnal the element
13887  * 
13888  * 
13889  * @constructor
13890  * Create a new CheckBox
13891  * @param {Object} config The config object
13892  */
13893
13894 Roo.bootstrap.CheckBox = function(config){
13895     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13896    
13897         this.addEvents({
13898             /**
13899             * @event check
13900             * Fires when the element is checked or unchecked.
13901             * @param {Roo.bootstrap.CheckBox} this This input
13902             * @param {Boolean} checked The new checked value
13903             */
13904            check : true
13905         });
13906 };
13907
13908 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13909     
13910     inputType: 'checkbox',
13911     inputValue: 1,
13912     valueOff: 0,
13913     boxLabel: false,
13914     checked: false,
13915     weight : false,
13916     
13917     getAutoCreate : function()
13918     {
13919         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13920         
13921         var id = Roo.id();
13922         
13923         var cfg = {};
13924         
13925         cfg.cls = 'form-group checkbox' //input-group
13926         
13927         
13928         
13929         
13930         var input =  {
13931             tag: 'input',
13932             id : id,
13933             type : this.inputType,
13934             value : (!this.checked) ? this.valueOff : this.inputValue,
13935             cls : 'roo-checkbox', //'form-box',
13936             placeholder : this.placeholder || ''
13937             
13938         };
13939         
13940         if (this.weight) { // Validity check?
13941             cfg.cls += " checkbox-" + this.weight;
13942         }
13943         
13944         if (this.disabled) {
13945             input.disabled=true;
13946         }
13947         
13948         if(this.checked){
13949             input.checked = this.checked;
13950         }
13951         
13952         if (this.name) {
13953             input.name = this.name;
13954         }
13955         
13956         if (this.size) {
13957             input.cls += ' input-' + this.size;
13958         }
13959         
13960         var settings=this;
13961         ['xs','sm','md','lg'].map(function(size){
13962             if (settings[size]) {
13963                 cfg.cls += ' col-' + size + '-' + settings[size];
13964             }
13965         });
13966         
13967        
13968         
13969         var inputblock = input;
13970         
13971         
13972         
13973         
13974         if (this.before || this.after) {
13975             
13976             inputblock = {
13977                 cls : 'input-group',
13978                 cn :  [] 
13979             };
13980             if (this.before) {
13981                 inputblock.cn.push({
13982                     tag :'span',
13983                     cls : 'input-group-addon',
13984                     html : this.before
13985                 });
13986             }
13987             inputblock.cn.push(input);
13988             if (this.after) {
13989                 inputblock.cn.push({
13990                     tag :'span',
13991                     cls : 'input-group-addon',
13992                     html : this.after
13993                 });
13994             }
13995             
13996         };
13997         
13998         if (align ==='left' && this.fieldLabel.length) {
13999                 Roo.log("left and has label");
14000                 cfg.cn = [
14001                     
14002                     {
14003                         tag: 'label',
14004                         'for' :  id,
14005                         cls : 'control-label col-md-' + this.labelWidth,
14006                         html : this.fieldLabel
14007                         
14008                     },
14009                     {
14010                         cls : "col-md-" + (12 - this.labelWidth), 
14011                         cn: [
14012                             inputblock
14013                         ]
14014                     }
14015                     
14016                 ];
14017         } else if ( this.fieldLabel.length) {
14018                 Roo.log(" label");
14019                 cfg.cn = [
14020                    
14021                     {
14022                         tag: this.boxLabel ? 'span' : 'label',
14023                         'for': id,
14024                         cls: 'control-label box-input-label',
14025                         //cls : 'input-group-addon',
14026                         html : this.fieldLabel
14027                         
14028                     },
14029                     
14030                     inputblock
14031                     
14032                 ];
14033
14034         } else {
14035             
14036                 Roo.log(" no label && no align");
14037                 cfg.cn = [  inputblock ] ;
14038                 
14039                 
14040         };
14041          if(this.boxLabel){
14042             cfg.cn.push( {
14043                 tag: 'label',
14044                 'for': id,
14045                 cls: 'box-label',
14046                 html: this.boxLabel
14047                 
14048             });
14049         }
14050         
14051         
14052        
14053         return cfg;
14054         
14055     },
14056     
14057     /**
14058      * return the real input element.
14059      */
14060     inputEl: function ()
14061     {
14062         return this.el.select('input.roo-checkbox',true).first();
14063     },
14064     
14065     label: function()
14066     {
14067         return this.el.select('label.control-label',true).first();
14068     },
14069     
14070     initEvents : function()
14071     {
14072 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
14073         
14074         this.inputEl().on('click', this.onClick,  this);
14075         
14076     },
14077     
14078     onClick : function()
14079     {   
14080         this.setChecked(!this.checked);
14081     },
14082     
14083     setChecked : function(state,suppressEvent)
14084     {
14085         this.checked = state;
14086         
14087         this.inputEl().dom.checked = state;
14088         
14089         if(suppressEvent !== true){
14090             this.fireEvent('check', this, state);
14091         }
14092         
14093         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14094         
14095     },
14096     
14097     setValue : function(v,suppressEvent)
14098     {
14099         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
14100     }
14101     
14102 });
14103
14104  
14105 /*
14106  * - LGPL
14107  *
14108  * Radio
14109  * 
14110  */
14111
14112 /**
14113  * @class Roo.bootstrap.Radio
14114  * @extends Roo.bootstrap.CheckBox
14115  * Bootstrap Radio class
14116
14117  * @constructor
14118  * Create a new Radio
14119  * @param {Object} config The config object
14120  */
14121
14122 Roo.bootstrap.Radio = function(config){
14123     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
14124    
14125 };
14126
14127 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
14128     
14129     inputType: 'radio',
14130     inputValue: '',
14131     valueOff: '',
14132     
14133     getAutoCreate : function()
14134     {
14135         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
14136         
14137         var id = Roo.id();
14138         
14139         var cfg = {};
14140         
14141         cfg.cls = 'form-group radio' //input-group
14142         
14143         var input =  {
14144             tag: 'input',
14145             id : id,
14146             type : this.inputType,
14147             value : (!this.checked) ? this.valueOff : this.inputValue,
14148             cls : 'roo-radio',
14149             placeholder : this.placeholder || ''
14150             
14151         };
14152           if (this.weight) { // Validity check?
14153             cfg.cls += " radio-" + this.weight;
14154         }
14155         if (this.disabled) {
14156             input.disabled=true;
14157         }
14158         
14159         if(this.checked){
14160             input.checked = this.checked;
14161         }
14162         
14163         if (this.name) {
14164             input.name = this.name;
14165         }
14166         
14167         if (this.size) {
14168             input.cls += ' input-' + this.size;
14169         }
14170         
14171         var settings=this;
14172         ['xs','sm','md','lg'].map(function(size){
14173             if (settings[size]) {
14174                 cfg.cls += ' col-' + size + '-' + settings[size];
14175             }
14176         });
14177         
14178         var inputblock = input;
14179         
14180         if (this.before || this.after) {
14181             
14182             inputblock = {
14183                 cls : 'input-group',
14184                 cn :  [] 
14185             };
14186             if (this.before) {
14187                 inputblock.cn.push({
14188                     tag :'span',
14189                     cls : 'input-group-addon',
14190                     html : this.before
14191                 });
14192             }
14193             inputblock.cn.push(input);
14194             if (this.after) {
14195                 inputblock.cn.push({
14196                     tag :'span',
14197                     cls : 'input-group-addon',
14198                     html : this.after
14199                 });
14200             }
14201             
14202         };
14203         
14204         if (align ==='left' && this.fieldLabel.length) {
14205                 Roo.log("left and has label");
14206                 cfg.cn = [
14207                     
14208                     {
14209                         tag: 'label',
14210                         'for' :  id,
14211                         cls : 'control-label col-md-' + this.labelWidth,
14212                         html : this.fieldLabel
14213                         
14214                     },
14215                     {
14216                         cls : "col-md-" + (12 - this.labelWidth), 
14217                         cn: [
14218                             inputblock
14219                         ]
14220                     }
14221                     
14222                 ];
14223         } else if ( this.fieldLabel.length) {
14224                 Roo.log(" label");
14225                  cfg.cn = [
14226                    
14227                     {
14228                         tag: 'label',
14229                         'for': id,
14230                         cls: 'control-label box-input-label',
14231                         //cls : 'input-group-addon',
14232                         html : this.fieldLabel
14233                         
14234                     },
14235                     
14236                     inputblock
14237                     
14238                 ];
14239
14240         } else {
14241             
14242                    Roo.log(" no label && no align");
14243                 cfg.cn = [
14244                     
14245                         inputblock
14246                     
14247                 ];
14248                 
14249                 
14250         };
14251         
14252         if(this.boxLabel){
14253             cfg.cn.push({
14254                 tag: 'label',
14255                 'for': id,
14256                 cls: 'box-label',
14257                 html: this.boxLabel
14258             })
14259         }
14260         
14261         return cfg;
14262         
14263     },
14264     inputEl: function ()
14265     {
14266         return this.el.select('input.roo-radio',true).first();
14267     },
14268     onClick : function()
14269     {   
14270         this.setChecked(true);
14271     },
14272     
14273     setChecked : function(state,suppressEvent)
14274     {
14275         if(state){
14276             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14277                 v.dom.checked = false;
14278             });
14279         }
14280         
14281         this.checked = state;
14282         this.inputEl().dom.checked = state;
14283         
14284         if(suppressEvent !== true){
14285             this.fireEvent('check', this, state);
14286         }
14287         
14288         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14289         
14290     },
14291     
14292     getGroupValue : function()
14293     {
14294         var value = ''
14295         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14296             if(v.dom.checked == true){
14297                 value = v.dom.value;
14298             }
14299         });
14300         
14301         return value;
14302     },
14303     
14304     /**
14305      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
14306      * @return {Mixed} value The field value
14307      */
14308     getValue : function(){
14309         return this.getGroupValue();
14310     }
14311     
14312 });
14313
14314  
14315 //<script type="text/javascript">
14316
14317 /*
14318  * Based  Ext JS Library 1.1.1
14319  * Copyright(c) 2006-2007, Ext JS, LLC.
14320  * LGPL
14321  *
14322  */
14323  
14324 /**
14325  * @class Roo.HtmlEditorCore
14326  * @extends Roo.Component
14327  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
14328  *
14329  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
14330  */
14331
14332 Roo.HtmlEditorCore = function(config){
14333     
14334     
14335     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
14336     this.addEvents({
14337         /**
14338          * @event initialize
14339          * Fires when the editor is fully initialized (including the iframe)
14340          * @param {Roo.HtmlEditorCore} this
14341          */
14342         initialize: true,
14343         /**
14344          * @event activate
14345          * Fires when the editor is first receives the focus. Any insertion must wait
14346          * until after this event.
14347          * @param {Roo.HtmlEditorCore} this
14348          */
14349         activate: true,
14350          /**
14351          * @event beforesync
14352          * Fires before the textarea is updated with content from the editor iframe. Return false
14353          * to cancel the sync.
14354          * @param {Roo.HtmlEditorCore} this
14355          * @param {String} html
14356          */
14357         beforesync: true,
14358          /**
14359          * @event beforepush
14360          * Fires before the iframe editor is updated with content from the textarea. Return false
14361          * to cancel the push.
14362          * @param {Roo.HtmlEditorCore} this
14363          * @param {String} html
14364          */
14365         beforepush: true,
14366          /**
14367          * @event sync
14368          * Fires when the textarea is updated with content from the editor iframe.
14369          * @param {Roo.HtmlEditorCore} this
14370          * @param {String} html
14371          */
14372         sync: true,
14373          /**
14374          * @event push
14375          * Fires when the iframe editor is updated with content from the textarea.
14376          * @param {Roo.HtmlEditorCore} this
14377          * @param {String} html
14378          */
14379         push: true,
14380         
14381         /**
14382          * @event editorevent
14383          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14384          * @param {Roo.HtmlEditorCore} this
14385          */
14386         editorevent: true
14387     });
14388      
14389 };
14390
14391
14392 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
14393
14394
14395      /**
14396      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
14397      */
14398     
14399     owner : false,
14400     
14401      /**
14402      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14403      *                        Roo.resizable.
14404      */
14405     resizable : false,
14406      /**
14407      * @cfg {Number} height (in pixels)
14408      */   
14409     height: 300,
14410    /**
14411      * @cfg {Number} width (in pixels)
14412      */   
14413     width: 500,
14414     
14415     /**
14416      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14417      * 
14418      */
14419     stylesheets: false,
14420     
14421     // id of frame..
14422     frameId: false,
14423     
14424     // private properties
14425     validationEvent : false,
14426     deferHeight: true,
14427     initialized : false,
14428     activated : false,
14429     sourceEditMode : false,
14430     onFocus : Roo.emptyFn,
14431     iframePad:3,
14432     hideMode:'offsets',
14433     
14434     clearUp: true,
14435     
14436      
14437     
14438
14439     /**
14440      * Protected method that will not generally be called directly. It
14441      * is called when the editor initializes the iframe with HTML contents. Override this method if you
14442      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
14443      */
14444     getDocMarkup : function(){
14445         // body styles..
14446         var st = '';
14447         Roo.log(this.stylesheets);
14448         
14449         // inherit styels from page...?? 
14450         if (this.stylesheets === false) {
14451             
14452             Roo.get(document.head).select('style').each(function(node) {
14453                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14454             });
14455             
14456             Roo.get(document.head).select('link').each(function(node) { 
14457                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14458             });
14459             
14460         } else if (!this.stylesheets.length) {
14461                 // simple..
14462                 st = '<style type="text/css">' +
14463                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14464                    '</style>';
14465         } else {
14466             Roo.each(this.stylesheets, function(s) {
14467                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
14468             });
14469             
14470         }
14471         
14472         st +=  '<style type="text/css">' +
14473             'IMG { cursor: pointer } ' +
14474         '</style>';
14475
14476         
14477         return '<html><head>' + st  +
14478             //<style type="text/css">' +
14479             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14480             //'</style>' +
14481             ' </head><body class="roo-htmleditor-body"></body></html>';
14482     },
14483
14484     // private
14485     onRender : function(ct, position)
14486     {
14487         var _t = this;
14488         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
14489         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
14490         
14491         
14492         this.el.dom.style.border = '0 none';
14493         this.el.dom.setAttribute('tabIndex', -1);
14494         this.el.addClass('x-hidden hide');
14495         
14496         
14497         
14498         if(Roo.isIE){ // fix IE 1px bogus margin
14499             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
14500         }
14501        
14502         
14503         this.frameId = Roo.id();
14504         
14505          
14506         
14507         var iframe = this.owner.wrap.createChild({
14508             tag: 'iframe',
14509             cls: 'form-control', // bootstrap..
14510             id: this.frameId,
14511             name: this.frameId,
14512             frameBorder : 'no',
14513             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
14514         }, this.el
14515         );
14516         
14517         
14518         this.iframe = iframe.dom;
14519
14520          this.assignDocWin();
14521         
14522         this.doc.designMode = 'on';
14523        
14524         this.doc.open();
14525         this.doc.write(this.getDocMarkup());
14526         this.doc.close();
14527
14528         
14529         var task = { // must defer to wait for browser to be ready
14530             run : function(){
14531                 //console.log("run task?" + this.doc.readyState);
14532                 this.assignDocWin();
14533                 if(this.doc.body || this.doc.readyState == 'complete'){
14534                     try {
14535                         this.doc.designMode="on";
14536                     } catch (e) {
14537                         return;
14538                     }
14539                     Roo.TaskMgr.stop(task);
14540                     this.initEditor.defer(10, this);
14541                 }
14542             },
14543             interval : 10,
14544             duration: 10000,
14545             scope: this
14546         };
14547         Roo.TaskMgr.start(task);
14548
14549         
14550          
14551     },
14552
14553     // private
14554     onResize : function(w, h)
14555     {
14556          Roo.log('resize: ' +w + ',' + h );
14557         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
14558         if(!this.iframe){
14559             return;
14560         }
14561         if(typeof w == 'number'){
14562             
14563             this.iframe.style.width = w + 'px';
14564         }
14565         if(typeof h == 'number'){
14566             
14567             this.iframe.style.height = h + 'px';
14568             if(this.doc){
14569                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
14570             }
14571         }
14572         
14573     },
14574
14575     /**
14576      * Toggles the editor between standard and source edit mode.
14577      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14578      */
14579     toggleSourceEdit : function(sourceEditMode){
14580         
14581         this.sourceEditMode = sourceEditMode === true;
14582         
14583         if(this.sourceEditMode){
14584  
14585             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
14586             
14587         }else{
14588             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
14589             //this.iframe.className = '';
14590             this.deferFocus();
14591         }
14592         //this.setSize(this.owner.wrap.getSize());
14593         //this.fireEvent('editmodechange', this, this.sourceEditMode);
14594     },
14595
14596     
14597   
14598
14599     /**
14600      * Protected method that will not generally be called directly. If you need/want
14601      * custom HTML cleanup, this is the method you should override.
14602      * @param {String} html The HTML to be cleaned
14603      * return {String} The cleaned HTML
14604      */
14605     cleanHtml : function(html){
14606         html = String(html);
14607         if(html.length > 5){
14608             if(Roo.isSafari){ // strip safari nonsense
14609                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
14610             }
14611         }
14612         if(html == '&nbsp;'){
14613             html = '';
14614         }
14615         return html;
14616     },
14617
14618     /**
14619      * HTML Editor -> Textarea
14620      * Protected method that will not generally be called directly. Syncs the contents
14621      * of the editor iframe with the textarea.
14622      */
14623     syncValue : function(){
14624         if(this.initialized){
14625             var bd = (this.doc.body || this.doc.documentElement);
14626             //this.cleanUpPaste(); -- this is done else where and causes havoc..
14627             var html = bd.innerHTML;
14628             if(Roo.isSafari){
14629                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
14630                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
14631                 if(m && m[1]){
14632                     html = '<div style="'+m[0]+'">' + html + '</div>';
14633                 }
14634             }
14635             html = this.cleanHtml(html);
14636             // fix up the special chars.. normaly like back quotes in word...
14637             // however we do not want to do this with chinese..
14638             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
14639                 var cc = b.charCodeAt();
14640                 if (
14641                     (cc >= 0x4E00 && cc < 0xA000 ) ||
14642                     (cc >= 0x3400 && cc < 0x4E00 ) ||
14643                     (cc >= 0xf900 && cc < 0xfb00 )
14644                 ) {
14645                         return b;
14646                 }
14647                 return "&#"+cc+";" 
14648             });
14649             if(this.owner.fireEvent('beforesync', this, html) !== false){
14650                 this.el.dom.value = html;
14651                 this.owner.fireEvent('sync', this, html);
14652             }
14653         }
14654     },
14655
14656     /**
14657      * Protected method that will not generally be called directly. Pushes the value of the textarea
14658      * into the iframe editor.
14659      */
14660     pushValue : function(){
14661         if(this.initialized){
14662             var v = this.el.dom.value.trim();
14663             
14664 //            if(v.length < 1){
14665 //                v = '&#160;';
14666 //            }
14667             
14668             if(this.owner.fireEvent('beforepush', this, v) !== false){
14669                 var d = (this.doc.body || this.doc.documentElement);
14670                 d.innerHTML = v;
14671                 this.cleanUpPaste();
14672                 this.el.dom.value = d.innerHTML;
14673                 this.owner.fireEvent('push', this, v);
14674             }
14675         }
14676     },
14677
14678     // private
14679     deferFocus : function(){
14680         this.focus.defer(10, this);
14681     },
14682
14683     // doc'ed in Field
14684     focus : function(){
14685         if(this.win && !this.sourceEditMode){
14686             this.win.focus();
14687         }else{
14688             this.el.focus();
14689         }
14690     },
14691     
14692     assignDocWin: function()
14693     {
14694         var iframe = this.iframe;
14695         
14696          if(Roo.isIE){
14697             this.doc = iframe.contentWindow.document;
14698             this.win = iframe.contentWindow;
14699         } else {
14700             if (!Roo.get(this.frameId)) {
14701                 return;
14702             }
14703             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14704             this.win = Roo.get(this.frameId).dom.contentWindow;
14705         }
14706     },
14707     
14708     // private
14709     initEditor : function(){
14710         //console.log("INIT EDITOR");
14711         this.assignDocWin();
14712         
14713         
14714         
14715         this.doc.designMode="on";
14716         this.doc.open();
14717         this.doc.write(this.getDocMarkup());
14718         this.doc.close();
14719         
14720         var dbody = (this.doc.body || this.doc.documentElement);
14721         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14722         // this copies styles from the containing element into thsi one..
14723         // not sure why we need all of this..
14724         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14725         
14726         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
14727         //ss['background-attachment'] = 'fixed'; // w3c
14728         dbody.bgProperties = 'fixed'; // ie
14729         //Roo.DomHelper.applyStyles(dbody, ss);
14730         Roo.EventManager.on(this.doc, {
14731             //'mousedown': this.onEditorEvent,
14732             'mouseup': this.onEditorEvent,
14733             'dblclick': this.onEditorEvent,
14734             'click': this.onEditorEvent,
14735             'keyup': this.onEditorEvent,
14736             buffer:100,
14737             scope: this
14738         });
14739         if(Roo.isGecko){
14740             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14741         }
14742         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14743             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14744         }
14745         this.initialized = true;
14746
14747         this.owner.fireEvent('initialize', this);
14748         this.pushValue();
14749     },
14750
14751     // private
14752     onDestroy : function(){
14753         
14754         
14755         
14756         if(this.rendered){
14757             
14758             //for (var i =0; i < this.toolbars.length;i++) {
14759             //    // fixme - ask toolbars for heights?
14760             //    this.toolbars[i].onDestroy();
14761            // }
14762             
14763             //this.wrap.dom.innerHTML = '';
14764             //this.wrap.remove();
14765         }
14766     },
14767
14768     // private
14769     onFirstFocus : function(){
14770         
14771         this.assignDocWin();
14772         
14773         
14774         this.activated = true;
14775          
14776     
14777         if(Roo.isGecko){ // prevent silly gecko errors
14778             this.win.focus();
14779             var s = this.win.getSelection();
14780             if(!s.focusNode || s.focusNode.nodeType != 3){
14781                 var r = s.getRangeAt(0);
14782                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14783                 r.collapse(true);
14784                 this.deferFocus();
14785             }
14786             try{
14787                 this.execCmd('useCSS', true);
14788                 this.execCmd('styleWithCSS', false);
14789             }catch(e){}
14790         }
14791         this.owner.fireEvent('activate', this);
14792     },
14793
14794     // private
14795     adjustFont: function(btn){
14796         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14797         //if(Roo.isSafari){ // safari
14798         //    adjust *= 2;
14799        // }
14800         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14801         if(Roo.isSafari){ // safari
14802             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14803             v =  (v < 10) ? 10 : v;
14804             v =  (v > 48) ? 48 : v;
14805             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14806             
14807         }
14808         
14809         
14810         v = Math.max(1, v+adjust);
14811         
14812         this.execCmd('FontSize', v  );
14813     },
14814
14815     onEditorEvent : function(e){
14816         this.owner.fireEvent('editorevent', this, e);
14817       //  this.updateToolbar();
14818         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14819     },
14820
14821     insertTag : function(tg)
14822     {
14823         // could be a bit smarter... -> wrap the current selected tRoo..
14824         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14825             
14826             range = this.createRange(this.getSelection());
14827             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14828             wrappingNode.appendChild(range.extractContents());
14829             range.insertNode(wrappingNode);
14830
14831             return;
14832             
14833             
14834             
14835         }
14836         this.execCmd("formatblock",   tg);
14837         
14838     },
14839     
14840     insertText : function(txt)
14841     {
14842         
14843         
14844         var range = this.createRange();
14845         range.deleteContents();
14846                //alert(Sender.getAttribute('label'));
14847                
14848         range.insertNode(this.doc.createTextNode(txt));
14849     } ,
14850     
14851      
14852
14853     /**
14854      * Executes a Midas editor command on the editor document and performs necessary focus and
14855      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14856      * @param {String} cmd The Midas command
14857      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14858      */
14859     relayCmd : function(cmd, value){
14860         this.win.focus();
14861         this.execCmd(cmd, value);
14862         this.owner.fireEvent('editorevent', this);
14863         //this.updateToolbar();
14864         this.owner.deferFocus();
14865     },
14866
14867     /**
14868      * Executes a Midas editor command directly on the editor document.
14869      * For visual commands, you should use {@link #relayCmd} instead.
14870      * <b>This should only be called after the editor is initialized.</b>
14871      * @param {String} cmd The Midas command
14872      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14873      */
14874     execCmd : function(cmd, value){
14875         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14876         this.syncValue();
14877     },
14878  
14879  
14880    
14881     /**
14882      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14883      * to insert tRoo.
14884      * @param {String} text | dom node.. 
14885      */
14886     insertAtCursor : function(text)
14887     {
14888         
14889         
14890         
14891         if(!this.activated){
14892             return;
14893         }
14894         /*
14895         if(Roo.isIE){
14896             this.win.focus();
14897             var r = this.doc.selection.createRange();
14898             if(r){
14899                 r.collapse(true);
14900                 r.pasteHTML(text);
14901                 this.syncValue();
14902                 this.deferFocus();
14903             
14904             }
14905             return;
14906         }
14907         */
14908         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14909             this.win.focus();
14910             
14911             
14912             // from jquery ui (MIT licenced)
14913             var range, node;
14914             var win = this.win;
14915             
14916             if (win.getSelection && win.getSelection().getRangeAt) {
14917                 range = win.getSelection().getRangeAt(0);
14918                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14919                 range.insertNode(node);
14920             } else if (win.document.selection && win.document.selection.createRange) {
14921                 // no firefox support
14922                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14923                 win.document.selection.createRange().pasteHTML(txt);
14924             } else {
14925                 // no firefox support
14926                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14927                 this.execCmd('InsertHTML', txt);
14928             } 
14929             
14930             this.syncValue();
14931             
14932             this.deferFocus();
14933         }
14934     },
14935  // private
14936     mozKeyPress : function(e){
14937         if(e.ctrlKey){
14938             var c = e.getCharCode(), cmd;
14939           
14940             if(c > 0){
14941                 c = String.fromCharCode(c).toLowerCase();
14942                 switch(c){
14943                     case 'b':
14944                         cmd = 'bold';
14945                         break;
14946                     case 'i':
14947                         cmd = 'italic';
14948                         break;
14949                     
14950                     case 'u':
14951                         cmd = 'underline';
14952                         break;
14953                     
14954                     case 'v':
14955                         this.cleanUpPaste.defer(100, this);
14956                         return;
14957                         
14958                 }
14959                 if(cmd){
14960                     this.win.focus();
14961                     this.execCmd(cmd);
14962                     this.deferFocus();
14963                     e.preventDefault();
14964                 }
14965                 
14966             }
14967         }
14968     },
14969
14970     // private
14971     fixKeys : function(){ // load time branching for fastest keydown performance
14972         if(Roo.isIE){
14973             return function(e){
14974                 var k = e.getKey(), r;
14975                 if(k == e.TAB){
14976                     e.stopEvent();
14977                     r = this.doc.selection.createRange();
14978                     if(r){
14979                         r.collapse(true);
14980                         r.pasteHTML('&#160;&#160;&#160;&#160;');
14981                         this.deferFocus();
14982                     }
14983                     return;
14984                 }
14985                 
14986                 if(k == e.ENTER){
14987                     r = this.doc.selection.createRange();
14988                     if(r){
14989                         var target = r.parentElement();
14990                         if(!target || target.tagName.toLowerCase() != 'li'){
14991                             e.stopEvent();
14992                             r.pasteHTML('<br />');
14993                             r.collapse(false);
14994                             r.select();
14995                         }
14996                     }
14997                 }
14998                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14999                     this.cleanUpPaste.defer(100, this);
15000                     return;
15001                 }
15002                 
15003                 
15004             };
15005         }else if(Roo.isOpera){
15006             return function(e){
15007                 var k = e.getKey();
15008                 if(k == e.TAB){
15009                     e.stopEvent();
15010                     this.win.focus();
15011                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
15012                     this.deferFocus();
15013                 }
15014                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15015                     this.cleanUpPaste.defer(100, this);
15016                     return;
15017                 }
15018                 
15019             };
15020         }else if(Roo.isSafari){
15021             return function(e){
15022                 var k = e.getKey();
15023                 
15024                 if(k == e.TAB){
15025                     e.stopEvent();
15026                     this.execCmd('InsertText','\t');
15027                     this.deferFocus();
15028                     return;
15029                 }
15030                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15031                     this.cleanUpPaste.defer(100, this);
15032                     return;
15033                 }
15034                 
15035              };
15036         }
15037     }(),
15038     
15039     getAllAncestors: function()
15040     {
15041         var p = this.getSelectedNode();
15042         var a = [];
15043         if (!p) {
15044             a.push(p); // push blank onto stack..
15045             p = this.getParentElement();
15046         }
15047         
15048         
15049         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
15050             a.push(p);
15051             p = p.parentNode;
15052         }
15053         a.push(this.doc.body);
15054         return a;
15055     },
15056     lastSel : false,
15057     lastSelNode : false,
15058     
15059     
15060     getSelection : function() 
15061     {
15062         this.assignDocWin();
15063         return Roo.isIE ? this.doc.selection : this.win.getSelection();
15064     },
15065     
15066     getSelectedNode: function() 
15067     {
15068         // this may only work on Gecko!!!
15069         
15070         // should we cache this!!!!
15071         
15072         
15073         
15074          
15075         var range = this.createRange(this.getSelection()).cloneRange();
15076         
15077         if (Roo.isIE) {
15078             var parent = range.parentElement();
15079             while (true) {
15080                 var testRange = range.duplicate();
15081                 testRange.moveToElementText(parent);
15082                 if (testRange.inRange(range)) {
15083                     break;
15084                 }
15085                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
15086                     break;
15087                 }
15088                 parent = parent.parentElement;
15089             }
15090             return parent;
15091         }
15092         
15093         // is ancestor a text element.
15094         var ac =  range.commonAncestorContainer;
15095         if (ac.nodeType == 3) {
15096             ac = ac.parentNode;
15097         }
15098         
15099         var ar = ac.childNodes;
15100          
15101         var nodes = [];
15102         var other_nodes = [];
15103         var has_other_nodes = false;
15104         for (var i=0;i<ar.length;i++) {
15105             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
15106                 continue;
15107             }
15108             // fullly contained node.
15109             
15110             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
15111                 nodes.push(ar[i]);
15112                 continue;
15113             }
15114             
15115             // probably selected..
15116             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
15117                 other_nodes.push(ar[i]);
15118                 continue;
15119             }
15120             // outer..
15121             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
15122                 continue;
15123             }
15124             
15125             
15126             has_other_nodes = true;
15127         }
15128         if (!nodes.length && other_nodes.length) {
15129             nodes= other_nodes;
15130         }
15131         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
15132             return false;
15133         }
15134         
15135         return nodes[0];
15136     },
15137     createRange: function(sel)
15138     {
15139         // this has strange effects when using with 
15140         // top toolbar - not sure if it's a great idea.
15141         //this.editor.contentWindow.focus();
15142         if (typeof sel != "undefined") {
15143             try {
15144                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
15145             } catch(e) {
15146                 return this.doc.createRange();
15147             }
15148         } else {
15149             return this.doc.createRange();
15150         }
15151     },
15152     getParentElement: function()
15153     {
15154         
15155         this.assignDocWin();
15156         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
15157         
15158         var range = this.createRange(sel);
15159          
15160         try {
15161             var p = range.commonAncestorContainer;
15162             while (p.nodeType == 3) { // text node
15163                 p = p.parentNode;
15164             }
15165             return p;
15166         } catch (e) {
15167             return null;
15168         }
15169     
15170     },
15171     /***
15172      *
15173      * Range intersection.. the hard stuff...
15174      *  '-1' = before
15175      *  '0' = hits..
15176      *  '1' = after.
15177      *         [ -- selected range --- ]
15178      *   [fail]                        [fail]
15179      *
15180      *    basically..
15181      *      if end is before start or  hits it. fail.
15182      *      if start is after end or hits it fail.
15183      *
15184      *   if either hits (but other is outside. - then it's not 
15185      *   
15186      *    
15187      **/
15188     
15189     
15190     // @see http://www.thismuchiknow.co.uk/?p=64.
15191     rangeIntersectsNode : function(range, node)
15192     {
15193         var nodeRange = node.ownerDocument.createRange();
15194         try {
15195             nodeRange.selectNode(node);
15196         } catch (e) {
15197             nodeRange.selectNodeContents(node);
15198         }
15199     
15200         var rangeStartRange = range.cloneRange();
15201         rangeStartRange.collapse(true);
15202     
15203         var rangeEndRange = range.cloneRange();
15204         rangeEndRange.collapse(false);
15205     
15206         var nodeStartRange = nodeRange.cloneRange();
15207         nodeStartRange.collapse(true);
15208     
15209         var nodeEndRange = nodeRange.cloneRange();
15210         nodeEndRange.collapse(false);
15211     
15212         return rangeStartRange.compareBoundaryPoints(
15213                  Range.START_TO_START, nodeEndRange) == -1 &&
15214                rangeEndRange.compareBoundaryPoints(
15215                  Range.START_TO_START, nodeStartRange) == 1;
15216         
15217          
15218     },
15219     rangeCompareNode : function(range, node)
15220     {
15221         var nodeRange = node.ownerDocument.createRange();
15222         try {
15223             nodeRange.selectNode(node);
15224         } catch (e) {
15225             nodeRange.selectNodeContents(node);
15226         }
15227         
15228         
15229         range.collapse(true);
15230     
15231         nodeRange.collapse(true);
15232      
15233         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
15234         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
15235          
15236         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
15237         
15238         var nodeIsBefore   =  ss == 1;
15239         var nodeIsAfter    = ee == -1;
15240         
15241         if (nodeIsBefore && nodeIsAfter)
15242             return 0; // outer
15243         if (!nodeIsBefore && nodeIsAfter)
15244             return 1; //right trailed.
15245         
15246         if (nodeIsBefore && !nodeIsAfter)
15247             return 2;  // left trailed.
15248         // fully contined.
15249         return 3;
15250     },
15251
15252     // private? - in a new class?
15253     cleanUpPaste :  function()
15254     {
15255         // cleans up the whole document..
15256         Roo.log('cleanuppaste');
15257         
15258         this.cleanUpChildren(this.doc.body);
15259         var clean = this.cleanWordChars(this.doc.body.innerHTML);
15260         if (clean != this.doc.body.innerHTML) {
15261             this.doc.body.innerHTML = clean;
15262         }
15263         
15264     },
15265     
15266     cleanWordChars : function(input) {// change the chars to hex code
15267         var he = Roo.HtmlEditorCore;
15268         
15269         var output = input;
15270         Roo.each(he.swapCodes, function(sw) { 
15271             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
15272             
15273             output = output.replace(swapper, sw[1]);
15274         });
15275         
15276         return output;
15277     },
15278     
15279     
15280     cleanUpChildren : function (n)
15281     {
15282         if (!n.childNodes.length) {
15283             return;
15284         }
15285         for (var i = n.childNodes.length-1; i > -1 ; i--) {
15286            this.cleanUpChild(n.childNodes[i]);
15287         }
15288     },
15289     
15290     
15291         
15292     
15293     cleanUpChild : function (node)
15294     {
15295         var ed = this;
15296         //console.log(node);
15297         if (node.nodeName == "#text") {
15298             // clean up silly Windows -- stuff?
15299             return; 
15300         }
15301         if (node.nodeName == "#comment") {
15302             node.parentNode.removeChild(node);
15303             // clean up silly Windows -- stuff?
15304             return; 
15305         }
15306         
15307         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
15308             // remove node.
15309             node.parentNode.removeChild(node);
15310             return;
15311             
15312         }
15313         
15314         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
15315         
15316         // remove <a name=....> as rendering on yahoo mailer is borked with this.
15317         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
15318         
15319         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
15320         //    remove_keep_children = true;
15321         //}
15322         
15323         if (remove_keep_children) {
15324             this.cleanUpChildren(node);
15325             // inserts everything just before this node...
15326             while (node.childNodes.length) {
15327                 var cn = node.childNodes[0];
15328                 node.removeChild(cn);
15329                 node.parentNode.insertBefore(cn, node);
15330             }
15331             node.parentNode.removeChild(node);
15332             return;
15333         }
15334         
15335         if (!node.attributes || !node.attributes.length) {
15336             this.cleanUpChildren(node);
15337             return;
15338         }
15339         
15340         function cleanAttr(n,v)
15341         {
15342             
15343             if (v.match(/^\./) || v.match(/^\//)) {
15344                 return;
15345             }
15346             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
15347                 return;
15348             }
15349             if (v.match(/^#/)) {
15350                 return;
15351             }
15352 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
15353             node.removeAttribute(n);
15354             
15355         }
15356         
15357         function cleanStyle(n,v)
15358         {
15359             if (v.match(/expression/)) { //XSS?? should we even bother..
15360                 node.removeAttribute(n);
15361                 return;
15362             }
15363             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
15364             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
15365             
15366             
15367             var parts = v.split(/;/);
15368             var clean = [];
15369             
15370             Roo.each(parts, function(p) {
15371                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
15372                 if (!p.length) {
15373                     return true;
15374                 }
15375                 var l = p.split(':').shift().replace(/\s+/g,'');
15376                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
15377                 
15378                 if ( cblack.indexOf(l) > -1) {
15379 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15380                     //node.removeAttribute(n);
15381                     return true;
15382                 }
15383                 //Roo.log()
15384                 // only allow 'c whitelisted system attributes'
15385                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
15386 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15387                     //node.removeAttribute(n);
15388                     return true;
15389                 }
15390                 
15391                 
15392                  
15393                 
15394                 clean.push(p);
15395                 return true;
15396             });
15397             if (clean.length) { 
15398                 node.setAttribute(n, clean.join(';'));
15399             } else {
15400                 node.removeAttribute(n);
15401             }
15402             
15403         }
15404         
15405         
15406         for (var i = node.attributes.length-1; i > -1 ; i--) {
15407             var a = node.attributes[i];
15408             //console.log(a);
15409             
15410             if (a.name.toLowerCase().substr(0,2)=='on')  {
15411                 node.removeAttribute(a.name);
15412                 continue;
15413             }
15414             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
15415                 node.removeAttribute(a.name);
15416                 continue;
15417             }
15418             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
15419                 cleanAttr(a.name,a.value); // fixme..
15420                 continue;
15421             }
15422             if (a.name == 'style') {
15423                 cleanStyle(a.name,a.value);
15424                 continue;
15425             }
15426             /// clean up MS crap..
15427             // tecnically this should be a list of valid class'es..
15428             
15429             
15430             if (a.name == 'class') {
15431                 if (a.value.match(/^Mso/)) {
15432                     node.className = '';
15433                 }
15434                 
15435                 if (a.value.match(/body/)) {
15436                     node.className = '';
15437                 }
15438                 continue;
15439             }
15440             
15441             // style cleanup!?
15442             // class cleanup?
15443             
15444         }
15445         
15446         
15447         this.cleanUpChildren(node);
15448         
15449         
15450     },
15451     /**
15452      * Clean up MS wordisms...
15453      */
15454     cleanWord : function(node)
15455     {
15456         var _t = this;
15457         var cleanWordChildren = function()
15458         {
15459             if (!node.childNodes.length) {
15460                 return;
15461             }
15462             for (var i = node.childNodes.length-1; i > -1 ; i--) {
15463                _t.cleanWord(node.childNodes[i]);
15464             }
15465         }
15466         
15467         
15468         if (!node) {
15469             this.cleanWord(this.doc.body);
15470             return;
15471         }
15472         if (node.nodeName == "#text") {
15473             // clean up silly Windows -- stuff?
15474             return; 
15475         }
15476         if (node.nodeName == "#comment") {
15477             node.parentNode.removeChild(node);
15478             // clean up silly Windows -- stuff?
15479             return; 
15480         }
15481         
15482         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
15483             node.parentNode.removeChild(node);
15484             return;
15485         }
15486         
15487         // remove - but keep children..
15488         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
15489             while (node.childNodes.length) {
15490                 var cn = node.childNodes[0];
15491                 node.removeChild(cn);
15492                 node.parentNode.insertBefore(cn, node);
15493             }
15494             node.parentNode.removeChild(node);
15495             cleanWordChildren();
15496             return;
15497         }
15498         // clean styles
15499         if (node.className.length) {
15500             
15501             var cn = node.className.split(/\W+/);
15502             var cna = [];
15503             Roo.each(cn, function(cls) {
15504                 if (cls.match(/Mso[a-zA-Z]+/)) {
15505                     return;
15506                 }
15507                 cna.push(cls);
15508             });
15509             node.className = cna.length ? cna.join(' ') : '';
15510             if (!cna.length) {
15511                 node.removeAttribute("class");
15512             }
15513         }
15514         
15515         if (node.hasAttribute("lang")) {
15516             node.removeAttribute("lang");
15517         }
15518         
15519         if (node.hasAttribute("style")) {
15520             
15521             var styles = node.getAttribute("style").split(";");
15522             var nstyle = [];
15523             Roo.each(styles, function(s) {
15524                 if (!s.match(/:/)) {
15525                     return;
15526                 }
15527                 var kv = s.split(":");
15528                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
15529                     return;
15530                 }
15531                 // what ever is left... we allow.
15532                 nstyle.push(s);
15533             });
15534             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
15535             if (!nstyle.length) {
15536                 node.removeAttribute('style');
15537             }
15538         }
15539         
15540         cleanWordChildren();
15541         
15542         
15543     },
15544     domToHTML : function(currentElement, depth, nopadtext) {
15545         
15546             depth = depth || 0;
15547             nopadtext = nopadtext || false;
15548         
15549             if (!currentElement) {
15550                 return this.domToHTML(this.doc.body);
15551             }
15552             
15553             //Roo.log(currentElement);
15554             var j;
15555             var allText = false;
15556             var nodeName = currentElement.nodeName;
15557             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
15558             
15559             if  (nodeName == '#text') {
15560                 return currentElement.nodeValue;
15561             }
15562             
15563             
15564             var ret = '';
15565             if (nodeName != 'BODY') {
15566                  
15567                 var i = 0;
15568                 // Prints the node tagName, such as <A>, <IMG>, etc
15569                 if (tagName) {
15570                     var attr = [];
15571                     for(i = 0; i < currentElement.attributes.length;i++) {
15572                         // quoting?
15573                         var aname = currentElement.attributes.item(i).name;
15574                         if (!currentElement.attributes.item(i).value.length) {
15575                             continue;
15576                         }
15577                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
15578                     }
15579                     
15580                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
15581                 } 
15582                 else {
15583                     
15584                     // eack
15585                 }
15586             } else {
15587                 tagName = false;
15588             }
15589             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
15590                 return ret;
15591             }
15592             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
15593                 nopadtext = true;
15594             }
15595             
15596             
15597             // Traverse the tree
15598             i = 0;
15599             var currentElementChild = currentElement.childNodes.item(i);
15600             var allText = true;
15601             var innerHTML  = '';
15602             lastnode = '';
15603             while (currentElementChild) {
15604                 // Formatting code (indent the tree so it looks nice on the screen)
15605                 var nopad = nopadtext;
15606                 if (lastnode == 'SPAN') {
15607                     nopad  = true;
15608                 }
15609                 // text
15610                 if  (currentElementChild.nodeName == '#text') {
15611                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
15612                     if (!nopad && toadd.length > 80) {
15613                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
15614                     }
15615                     innerHTML  += toadd;
15616                     
15617                     i++;
15618                     currentElementChild = currentElement.childNodes.item(i);
15619                     lastNode = '';
15620                     continue;
15621                 }
15622                 allText = false;
15623                 
15624                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
15625                     
15626                 // Recursively traverse the tree structure of the child node
15627                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
15628                 lastnode = currentElementChild.nodeName;
15629                 i++;
15630                 currentElementChild=currentElement.childNodes.item(i);
15631             }
15632             
15633             ret += innerHTML;
15634             
15635             if (!allText) {
15636                     // The remaining code is mostly for formatting the tree
15637                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
15638             }
15639             
15640             
15641             if (tagName) {
15642                 ret+= "</"+tagName+">";
15643             }
15644             return ret;
15645             
15646         }
15647     
15648     // hide stuff that is not compatible
15649     /**
15650      * @event blur
15651      * @hide
15652      */
15653     /**
15654      * @event change
15655      * @hide
15656      */
15657     /**
15658      * @event focus
15659      * @hide
15660      */
15661     /**
15662      * @event specialkey
15663      * @hide
15664      */
15665     /**
15666      * @cfg {String} fieldClass @hide
15667      */
15668     /**
15669      * @cfg {String} focusClass @hide
15670      */
15671     /**
15672      * @cfg {String} autoCreate @hide
15673      */
15674     /**
15675      * @cfg {String} inputType @hide
15676      */
15677     /**
15678      * @cfg {String} invalidClass @hide
15679      */
15680     /**
15681      * @cfg {String} invalidText @hide
15682      */
15683     /**
15684      * @cfg {String} msgFx @hide
15685      */
15686     /**
15687      * @cfg {String} validateOnBlur @hide
15688      */
15689 });
15690
15691 Roo.HtmlEditorCore.white = [
15692         'area', 'br', 'img', 'input', 'hr', 'wbr',
15693         
15694        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
15695        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
15696        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
15697        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
15698        'table',   'ul',         'xmp', 
15699        
15700        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
15701       'thead',   'tr', 
15702      
15703       'dir', 'menu', 'ol', 'ul', 'dl',
15704        
15705       'embed',  'object'
15706 ];
15707
15708
15709 Roo.HtmlEditorCore.black = [
15710     //    'embed',  'object', // enable - backend responsiblity to clean thiese
15711         'applet', // 
15712         'base',   'basefont', 'bgsound', 'blink',  'body', 
15713         'frame',  'frameset', 'head',    'html',   'ilayer', 
15714         'iframe', 'layer',  'link',     'meta',    'object',   
15715         'script', 'style' ,'title',  'xml' // clean later..
15716 ];
15717 Roo.HtmlEditorCore.clean = [
15718     'script', 'style', 'title', 'xml'
15719 ];
15720 Roo.HtmlEditorCore.remove = [
15721     'font'
15722 ];
15723 // attributes..
15724
15725 Roo.HtmlEditorCore.ablack = [
15726     'on'
15727 ];
15728     
15729 Roo.HtmlEditorCore.aclean = [ 
15730     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
15731 ];
15732
15733 // protocols..
15734 Roo.HtmlEditorCore.pwhite= [
15735         'http',  'https',  'mailto'
15736 ];
15737
15738 // white listed style attributes.
15739 Roo.HtmlEditorCore.cwhite= [
15740       //  'text-align', /// default is to allow most things..
15741       
15742          
15743 //        'font-size'//??
15744 ];
15745
15746 // black listed style attributes.
15747 Roo.HtmlEditorCore.cblack= [
15748       //  'font-size' -- this can be set by the project 
15749 ];
15750
15751
15752 Roo.HtmlEditorCore.swapCodes   =[ 
15753     [    8211, "--" ], 
15754     [    8212, "--" ], 
15755     [    8216,  "'" ],  
15756     [    8217, "'" ],  
15757     [    8220, '"' ],  
15758     [    8221, '"' ],  
15759     [    8226, "*" ],  
15760     [    8230, "..." ]
15761 ]; 
15762
15763     /*
15764  * - LGPL
15765  *
15766  * HtmlEditor
15767  * 
15768  */
15769
15770 /**
15771  * @class Roo.bootstrap.HtmlEditor
15772  * @extends Roo.bootstrap.TextArea
15773  * Bootstrap HtmlEditor class
15774
15775  * @constructor
15776  * Create a new HtmlEditor
15777  * @param {Object} config The config object
15778  */
15779
15780 Roo.bootstrap.HtmlEditor = function(config){
15781     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
15782     if (!this.toolbars) {
15783         this.toolbars = [];
15784     }
15785     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
15786     this.addEvents({
15787             /**
15788              * @event initialize
15789              * Fires when the editor is fully initialized (including the iframe)
15790              * @param {HtmlEditor} this
15791              */
15792             initialize: true,
15793             /**
15794              * @event activate
15795              * Fires when the editor is first receives the focus. Any insertion must wait
15796              * until after this event.
15797              * @param {HtmlEditor} this
15798              */
15799             activate: true,
15800              /**
15801              * @event beforesync
15802              * Fires before the textarea is updated with content from the editor iframe. Return false
15803              * to cancel the sync.
15804              * @param {HtmlEditor} this
15805              * @param {String} html
15806              */
15807             beforesync: true,
15808              /**
15809              * @event beforepush
15810              * Fires before the iframe editor is updated with content from the textarea. Return false
15811              * to cancel the push.
15812              * @param {HtmlEditor} this
15813              * @param {String} html
15814              */
15815             beforepush: true,
15816              /**
15817              * @event sync
15818              * Fires when the textarea is updated with content from the editor iframe.
15819              * @param {HtmlEditor} this
15820              * @param {String} html
15821              */
15822             sync: true,
15823              /**
15824              * @event push
15825              * Fires when the iframe editor is updated with content from the textarea.
15826              * @param {HtmlEditor} this
15827              * @param {String} html
15828              */
15829             push: true,
15830              /**
15831              * @event editmodechange
15832              * Fires when the editor switches edit modes
15833              * @param {HtmlEditor} this
15834              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
15835              */
15836             editmodechange: true,
15837             /**
15838              * @event editorevent
15839              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15840              * @param {HtmlEditor} this
15841              */
15842             editorevent: true,
15843             /**
15844              * @event firstfocus
15845              * Fires when on first focus - needed by toolbars..
15846              * @param {HtmlEditor} this
15847              */
15848             firstfocus: true,
15849             /**
15850              * @event autosave
15851              * Auto save the htmlEditor value as a file into Events
15852              * @param {HtmlEditor} this
15853              */
15854             autosave: true,
15855             /**
15856              * @event savedpreview
15857              * preview the saved version of htmlEditor
15858              * @param {HtmlEditor} this
15859              */
15860             savedpreview: true
15861         });
15862 };
15863
15864
15865 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
15866     
15867     
15868       /**
15869      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15870      */
15871     toolbars : false,
15872    
15873      /**
15874      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15875      *                        Roo.resizable.
15876      */
15877     resizable : false,
15878      /**
15879      * @cfg {Number} height (in pixels)
15880      */   
15881     height: 300,
15882    /**
15883      * @cfg {Number} width (in pixels)
15884      */   
15885     width: false,
15886     
15887     /**
15888      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15889      * 
15890      */
15891     stylesheets: false,
15892     
15893     // id of frame..
15894     frameId: false,
15895     
15896     // private properties
15897     validationEvent : false,
15898     deferHeight: true,
15899     initialized : false,
15900     activated : false,
15901     
15902     onFocus : Roo.emptyFn,
15903     iframePad:3,
15904     hideMode:'offsets',
15905     
15906     
15907     tbContainer : false,
15908     
15909     toolbarContainer :function() {
15910         return this.wrap.select('.x-html-editor-tb',true).first();
15911     },
15912
15913     /**
15914      * Protected method that will not generally be called directly. It
15915      * is called when the editor creates its toolbar. Override this method if you need to
15916      * add custom toolbar buttons.
15917      * @param {HtmlEditor} editor
15918      */
15919     createToolbar : function(){
15920         
15921         Roo.log("create toolbars");
15922         
15923         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
15924         this.toolbars[0].render(this.toolbarContainer());
15925         
15926         return;
15927         
15928 //        if (!editor.toolbars || !editor.toolbars.length) {
15929 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15930 //        }
15931 //        
15932 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15933 //            editor.toolbars[i] = Roo.factory(
15934 //                    typeof(editor.toolbars[i]) == 'string' ?
15935 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15936 //                Roo.bootstrap.HtmlEditor);
15937 //            editor.toolbars[i].init(editor);
15938 //        }
15939     },
15940
15941      
15942     // private
15943     onRender : function(ct, position)
15944     {
15945        // Roo.log("Call onRender: " + this.xtype);
15946         var _t = this;
15947         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15948       
15949         this.wrap = this.inputEl().wrap({
15950             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15951         });
15952         
15953         this.editorcore.onRender(ct, position);
15954          
15955         if (this.resizable) {
15956             this.resizeEl = new Roo.Resizable(this.wrap, {
15957                 pinned : true,
15958                 wrap: true,
15959                 dynamic : true,
15960                 minHeight : this.height,
15961                 height: this.height,
15962                 handles : this.resizable,
15963                 width: this.width,
15964                 listeners : {
15965                     resize : function(r, w, h) {
15966                         _t.onResize(w,h); // -something
15967                     }
15968                 }
15969             });
15970             
15971         }
15972         this.createToolbar(this);
15973        
15974         
15975         if(!this.width && this.resizable){
15976             this.setSize(this.wrap.getSize());
15977         }
15978         if (this.resizeEl) {
15979             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15980             // should trigger onReize..
15981         }
15982         
15983     },
15984
15985     // private
15986     onResize : function(w, h)
15987     {
15988         Roo.log('resize: ' +w + ',' + h );
15989         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15990         var ew = false;
15991         var eh = false;
15992         
15993         if(this.inputEl() ){
15994             if(typeof w == 'number'){
15995                 var aw = w - this.wrap.getFrameWidth('lr');
15996                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15997                 ew = aw;
15998             }
15999             if(typeof h == 'number'){
16000                  var tbh = -11;  // fixme it needs to tool bar size!
16001                 for (var i =0; i < this.toolbars.length;i++) {
16002                     // fixme - ask toolbars for heights?
16003                     tbh += this.toolbars[i].el.getHeight();
16004                     //if (this.toolbars[i].footer) {
16005                     //    tbh += this.toolbars[i].footer.el.getHeight();
16006                     //}
16007                 }
16008               
16009                 
16010                 
16011                 
16012                 
16013                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
16014                 ah -= 5; // knock a few pixes off for look..
16015                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
16016                 var eh = ah;
16017             }
16018         }
16019         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
16020         this.editorcore.onResize(ew,eh);
16021         
16022     },
16023
16024     /**
16025      * Toggles the editor between standard and source edit mode.
16026      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16027      */
16028     toggleSourceEdit : function(sourceEditMode)
16029     {
16030         this.editorcore.toggleSourceEdit(sourceEditMode);
16031         
16032         if(this.editorcore.sourceEditMode){
16033             Roo.log('editor - showing textarea');
16034             
16035 //            Roo.log('in');
16036 //            Roo.log(this.syncValue());
16037             this.syncValue();
16038             this.inputEl().removeClass('hide');
16039             this.inputEl().dom.removeAttribute('tabIndex');
16040             this.inputEl().focus();
16041         }else{
16042             Roo.log('editor - hiding textarea');
16043 //            Roo.log('out')
16044 //            Roo.log(this.pushValue()); 
16045             this.pushValue();
16046             
16047             this.inputEl().addClass('hide');
16048             this.inputEl().dom.setAttribute('tabIndex', -1);
16049             //this.deferFocus();
16050         }
16051          
16052         if(this.resizable){
16053             this.setSize(this.wrap.getSize());
16054         }
16055         
16056         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
16057     },
16058  
16059     // private (for BoxComponent)
16060     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16061
16062     // private (for BoxComponent)
16063     getResizeEl : function(){
16064         return this.wrap;
16065     },
16066
16067     // private (for BoxComponent)
16068     getPositionEl : function(){
16069         return this.wrap;
16070     },
16071
16072     // private
16073     initEvents : function(){
16074         this.originalValue = this.getValue();
16075     },
16076
16077 //    /**
16078 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16079 //     * @method
16080 //     */
16081 //    markInvalid : Roo.emptyFn,
16082 //    /**
16083 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16084 //     * @method
16085 //     */
16086 //    clearInvalid : Roo.emptyFn,
16087
16088     setValue : function(v){
16089         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
16090         this.editorcore.pushValue();
16091     },
16092
16093      
16094     // private
16095     deferFocus : function(){
16096         this.focus.defer(10, this);
16097     },
16098
16099     // doc'ed in Field
16100     focus : function(){
16101         this.editorcore.focus();
16102         
16103     },
16104       
16105
16106     // private
16107     onDestroy : function(){
16108         
16109         
16110         
16111         if(this.rendered){
16112             
16113             for (var i =0; i < this.toolbars.length;i++) {
16114                 // fixme - ask toolbars for heights?
16115                 this.toolbars[i].onDestroy();
16116             }
16117             
16118             this.wrap.dom.innerHTML = '';
16119             this.wrap.remove();
16120         }
16121     },
16122
16123     // private
16124     onFirstFocus : function(){
16125         //Roo.log("onFirstFocus");
16126         this.editorcore.onFirstFocus();
16127          for (var i =0; i < this.toolbars.length;i++) {
16128             this.toolbars[i].onFirstFocus();
16129         }
16130         
16131     },
16132     
16133     // private
16134     syncValue : function()
16135     {   
16136         this.editorcore.syncValue();
16137     },
16138     
16139     pushValue : function()
16140     {   
16141         this.editorcore.pushValue();
16142     }
16143      
16144     
16145     // hide stuff that is not compatible
16146     /**
16147      * @event blur
16148      * @hide
16149      */
16150     /**
16151      * @event change
16152      * @hide
16153      */
16154     /**
16155      * @event focus
16156      * @hide
16157      */
16158     /**
16159      * @event specialkey
16160      * @hide
16161      */
16162     /**
16163      * @cfg {String} fieldClass @hide
16164      */
16165     /**
16166      * @cfg {String} focusClass @hide
16167      */
16168     /**
16169      * @cfg {String} autoCreate @hide
16170      */
16171     /**
16172      * @cfg {String} inputType @hide
16173      */
16174     /**
16175      * @cfg {String} invalidClass @hide
16176      */
16177     /**
16178      * @cfg {String} invalidText @hide
16179      */
16180     /**
16181      * @cfg {String} msgFx @hide
16182      */
16183     /**
16184      * @cfg {String} validateOnBlur @hide
16185      */
16186 });
16187  
16188     
16189    
16190    
16191    
16192       
16193 Roo.namespace('Roo.bootstrap.htmleditor');
16194 /**
16195  * @class Roo.bootstrap.HtmlEditorToolbar1
16196  * Basic Toolbar
16197  * 
16198  * Usage:
16199  *
16200  new Roo.bootstrap.HtmlEditor({
16201     ....
16202     toolbars : [
16203         new Roo.bootstrap.HtmlEditorToolbar1({
16204             disable : { fonts: 1 , format: 1, ..., ... , ...],
16205             btns : [ .... ]
16206         })
16207     }
16208      
16209  * 
16210  * @cfg {Object} disable List of elements to disable..
16211  * @cfg {Array} btns List of additional buttons.
16212  * 
16213  * 
16214  * NEEDS Extra CSS? 
16215  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
16216  */
16217  
16218 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
16219 {
16220     
16221     Roo.apply(this, config);
16222     
16223     // default disabled, based on 'good practice'..
16224     this.disable = this.disable || {};
16225     Roo.applyIf(this.disable, {
16226         fontSize : true,
16227         colors : true,
16228         specialElements : true
16229     });
16230     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
16231     
16232     this.editor = config.editor;
16233     this.editorcore = config.editor.editorcore;
16234     
16235     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
16236     
16237     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
16238     // dont call parent... till later.
16239 }
16240 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
16241      
16242     bar : true,
16243     
16244     editor : false,
16245     editorcore : false,
16246     
16247     
16248     formats : [
16249         "p" ,  
16250         "h1","h2","h3","h4","h5","h6", 
16251         "pre", "code", 
16252         "abbr", "acronym", "address", "cite", "samp", "var",
16253         'div','span'
16254     ],
16255     
16256     onRender : function(ct, position)
16257     {
16258        // Roo.log("Call onRender: " + this.xtype);
16259         
16260        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
16261        Roo.log(this.el);
16262        this.el.dom.style.marginBottom = '0';
16263        var _this = this;
16264        var editorcore = this.editorcore;
16265        var editor= this.editor;
16266        
16267        var children = [];
16268        var btn = function(id,cmd , toggle, handler){
16269        
16270             var  event = toggle ? 'toggle' : 'click';
16271        
16272             var a = {
16273                 size : 'sm',
16274                 xtype: 'Button',
16275                 xns: Roo.bootstrap,
16276                 glyphicon : id,
16277                 cmd : id || cmd,
16278                 enableToggle:toggle !== false,
16279                 //html : 'submit'
16280                 pressed : toggle ? false : null,
16281                 listeners : {}
16282             }
16283             a.listeners[toggle ? 'toggle' : 'click'] = function() {
16284                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
16285             }
16286             children.push(a);
16287             return a;
16288        }
16289         
16290         var style = {
16291                 xtype: 'Button',
16292                 size : 'sm',
16293                 xns: Roo.bootstrap,
16294                 glyphicon : 'font',
16295                 //html : 'submit'
16296                 menu : {
16297                     xtype: 'Menu',
16298                     xns: Roo.bootstrap,
16299                     items:  []
16300                 }
16301         };
16302         Roo.each(this.formats, function(f) {
16303             style.menu.items.push({
16304                 xtype :'MenuItem',
16305                 xns: Roo.bootstrap,
16306                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
16307                 tagname : f,
16308                 listeners : {
16309                     click : function()
16310                     {
16311                         editorcore.insertTag(this.tagname);
16312                         editor.focus();
16313                     }
16314                 }
16315                 
16316             });
16317         });
16318          children.push(style);   
16319             
16320             
16321         btn('bold',false,true);
16322         btn('italic',false,true);
16323         btn('align-left', 'justifyleft',true);
16324         btn('align-center', 'justifycenter',true);
16325         btn('align-right' , 'justifyright',true);
16326         btn('link', false, false, function(btn) {
16327             //Roo.log("create link?");
16328             var url = prompt(this.createLinkText, this.defaultLinkValue);
16329             if(url && url != 'http:/'+'/'){
16330                 this.editorcore.relayCmd('createlink', url);
16331             }
16332         }),
16333         btn('list','insertunorderedlist',true);
16334         btn('pencil', false,true, function(btn){
16335                 Roo.log(this);
16336                 
16337                 this.toggleSourceEdit(btn.pressed);
16338         });
16339         /*
16340         var cog = {
16341                 xtype: 'Button',
16342                 size : 'sm',
16343                 xns: Roo.bootstrap,
16344                 glyphicon : 'cog',
16345                 //html : 'submit'
16346                 menu : {
16347                     xtype: 'Menu',
16348                     xns: Roo.bootstrap,
16349                     items:  []
16350                 }
16351         };
16352         
16353         cog.menu.items.push({
16354             xtype :'MenuItem',
16355             xns: Roo.bootstrap,
16356             html : Clean styles,
16357             tagname : f,
16358             listeners : {
16359                 click : function()
16360                 {
16361                     editorcore.insertTag(this.tagname);
16362                     editor.focus();
16363                 }
16364             }
16365             
16366         });
16367        */
16368         
16369          
16370        this.xtype = 'NavSimplebar';
16371         
16372         for(var i=0;i< children.length;i++) {
16373             
16374             this.buttons.add(this.addxtypeChild(children[i]));
16375             
16376         }
16377         
16378         editor.on('editorevent', this.updateToolbar, this);
16379     },
16380     onBtnClick : function(id)
16381     {
16382        this.editorcore.relayCmd(id);
16383        this.editorcore.focus();
16384     },
16385     
16386     /**
16387      * Protected method that will not generally be called directly. It triggers
16388      * a toolbar update by reading the markup state of the current selection in the editor.
16389      */
16390     updateToolbar: function(){
16391
16392         if(!this.editorcore.activated){
16393             this.editor.onFirstFocus(); // is this neeed?
16394             return;
16395         }
16396
16397         var btns = this.buttons; 
16398         var doc = this.editorcore.doc;
16399         btns.get('bold').setActive(doc.queryCommandState('bold'));
16400         btns.get('italic').setActive(doc.queryCommandState('italic'));
16401         //btns.get('underline').setActive(doc.queryCommandState('underline'));
16402         
16403         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
16404         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
16405         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
16406         
16407         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
16408         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
16409          /*
16410         
16411         var ans = this.editorcore.getAllAncestors();
16412         if (this.formatCombo) {
16413             
16414             
16415             var store = this.formatCombo.store;
16416             this.formatCombo.setValue("");
16417             for (var i =0; i < ans.length;i++) {
16418                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
16419                     // select it..
16420                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
16421                     break;
16422                 }
16423             }
16424         }
16425         
16426         
16427         
16428         // hides menus... - so this cant be on a menu...
16429         Roo.bootstrap.MenuMgr.hideAll();
16430         */
16431         Roo.bootstrap.MenuMgr.hideAll();
16432         //this.editorsyncValue();
16433     },
16434     onFirstFocus: function() {
16435         this.buttons.each(function(item){
16436            item.enable();
16437         });
16438     },
16439     toggleSourceEdit : function(sourceEditMode){
16440         
16441           
16442         if(sourceEditMode){
16443             Roo.log("disabling buttons");
16444            this.buttons.each( function(item){
16445                 if(item.cmd != 'pencil'){
16446                     item.disable();
16447                 }
16448             });
16449           
16450         }else{
16451             Roo.log("enabling buttons");
16452             if(this.editorcore.initialized){
16453                 this.buttons.each( function(item){
16454                     item.enable();
16455                 });
16456             }
16457             
16458         }
16459         Roo.log("calling toggole on editor");
16460         // tell the editor that it's been pressed..
16461         this.editor.toggleSourceEdit(sourceEditMode);
16462        
16463     }
16464 });
16465
16466
16467
16468
16469 /*
16470  * Based on:
16471  * Ext JS Library 1.1.1
16472  * Copyright(c) 2006-2007, Ext JS, LLC.
16473  *
16474  * Originally Released Under LGPL - original licence link has changed is not relivant.
16475  *
16476  * Fork - LGPL
16477  * <script type="text/javascript">
16478  */
16479  
16480
16481 /**
16482  * @class Roo.grid.ColumnModel
16483  * @extends Roo.util.Observable
16484  * This is the default implementation of a ColumnModel used by the Grid. It defines
16485  * the columns in the grid.
16486  * <br>Usage:<br>
16487  <pre><code>
16488  var colModel = new Roo.grid.ColumnModel([
16489         {header: "Ticker", width: 60, sortable: true, locked: true},
16490         {header: "Company Name", width: 150, sortable: true},
16491         {header: "Market Cap.", width: 100, sortable: true},
16492         {header: "$ Sales", width: 100, sortable: true, renderer: money},
16493         {header: "Employees", width: 100, sortable: true, resizable: false}
16494  ]);
16495  </code></pre>
16496  * <p>
16497  
16498  * The config options listed for this class are options which may appear in each
16499  * individual column definition.
16500  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
16501  * @constructor
16502  * @param {Object} config An Array of column config objects. See this class's
16503  * config objects for details.
16504 */
16505 Roo.grid.ColumnModel = function(config){
16506         /**
16507      * The config passed into the constructor
16508      */
16509     this.config = config;
16510     this.lookup = {};
16511
16512     // if no id, create one
16513     // if the column does not have a dataIndex mapping,
16514     // map it to the order it is in the config
16515     for(var i = 0, len = config.length; i < len; i++){
16516         var c = config[i];
16517         if(typeof c.dataIndex == "undefined"){
16518             c.dataIndex = i;
16519         }
16520         if(typeof c.renderer == "string"){
16521             c.renderer = Roo.util.Format[c.renderer];
16522         }
16523         if(typeof c.id == "undefined"){
16524             c.id = Roo.id();
16525         }
16526         if(c.editor && c.editor.xtype){
16527             c.editor  = Roo.factory(c.editor, Roo.grid);
16528         }
16529         if(c.editor && c.editor.isFormField){
16530             c.editor = new Roo.grid.GridEditor(c.editor);
16531         }
16532         this.lookup[c.id] = c;
16533     }
16534
16535     /**
16536      * The width of columns which have no width specified (defaults to 100)
16537      * @type Number
16538      */
16539     this.defaultWidth = 100;
16540
16541     /**
16542      * Default sortable of columns which have no sortable specified (defaults to false)
16543      * @type Boolean
16544      */
16545     this.defaultSortable = false;
16546
16547     this.addEvents({
16548         /**
16549              * @event widthchange
16550              * Fires when the width of a column changes.
16551              * @param {ColumnModel} this
16552              * @param {Number} columnIndex The column index
16553              * @param {Number} newWidth The new width
16554              */
16555             "widthchange": true,
16556         /**
16557              * @event headerchange
16558              * Fires when the text of a header changes.
16559              * @param {ColumnModel} this
16560              * @param {Number} columnIndex The column index
16561              * @param {Number} newText The new header text
16562              */
16563             "headerchange": true,
16564         /**
16565              * @event hiddenchange
16566              * Fires when a column is hidden or "unhidden".
16567              * @param {ColumnModel} this
16568              * @param {Number} columnIndex The column index
16569              * @param {Boolean} hidden true if hidden, false otherwise
16570              */
16571             "hiddenchange": true,
16572             /**
16573          * @event columnmoved
16574          * Fires when a column is moved.
16575          * @param {ColumnModel} this
16576          * @param {Number} oldIndex
16577          * @param {Number} newIndex
16578          */
16579         "columnmoved" : true,
16580         /**
16581          * @event columlockchange
16582          * Fires when a column's locked state is changed
16583          * @param {ColumnModel} this
16584          * @param {Number} colIndex
16585          * @param {Boolean} locked true if locked
16586          */
16587         "columnlockchange" : true
16588     });
16589     Roo.grid.ColumnModel.superclass.constructor.call(this);
16590 };
16591 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
16592     /**
16593      * @cfg {String} header The header text to display in the Grid view.
16594      */
16595     /**
16596      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
16597      * {@link Roo.data.Record} definition from which to draw the column's value. If not
16598      * specified, the column's index is used as an index into the Record's data Array.
16599      */
16600     /**
16601      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
16602      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
16603      */
16604     /**
16605      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
16606      * Defaults to the value of the {@link #defaultSortable} property.
16607      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
16608      */
16609     /**
16610      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
16611      */
16612     /**
16613      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
16614      */
16615     /**
16616      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
16617      */
16618     /**
16619      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
16620      */
16621     /**
16622      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
16623      * given the cell's data value. See {@link #setRenderer}. If not specified, the
16624      * default renderer uses the raw data value.
16625      */
16626        /**
16627      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
16628      */
16629     /**
16630      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
16631      */
16632
16633     /**
16634      * Returns the id of the column at the specified index.
16635      * @param {Number} index The column index
16636      * @return {String} the id
16637      */
16638     getColumnId : function(index){
16639         return this.config[index].id;
16640     },
16641
16642     /**
16643      * Returns the column for a specified id.
16644      * @param {String} id The column id
16645      * @return {Object} the column
16646      */
16647     getColumnById : function(id){
16648         return this.lookup[id];
16649     },
16650
16651     
16652     /**
16653      * Returns the column for a specified dataIndex.
16654      * @param {String} dataIndex The column dataIndex
16655      * @return {Object|Boolean} the column or false if not found
16656      */
16657     getColumnByDataIndex: function(dataIndex){
16658         var index = this.findColumnIndex(dataIndex);
16659         return index > -1 ? this.config[index] : false;
16660     },
16661     
16662     /**
16663      * Returns the index for a specified column id.
16664      * @param {String} id The column id
16665      * @return {Number} the index, or -1 if not found
16666      */
16667     getIndexById : function(id){
16668         for(var i = 0, len = this.config.length; i < len; i++){
16669             if(this.config[i].id == id){
16670                 return i;
16671             }
16672         }
16673         return -1;
16674     },
16675     
16676     /**
16677      * Returns the index for a specified column dataIndex.
16678      * @param {String} dataIndex The column dataIndex
16679      * @return {Number} the index, or -1 if not found
16680      */
16681     
16682     findColumnIndex : function(dataIndex){
16683         for(var i = 0, len = this.config.length; i < len; i++){
16684             if(this.config[i].dataIndex == dataIndex){
16685                 return i;
16686             }
16687         }
16688         return -1;
16689     },
16690     
16691     
16692     moveColumn : function(oldIndex, newIndex){
16693         var c = this.config[oldIndex];
16694         this.config.splice(oldIndex, 1);
16695         this.config.splice(newIndex, 0, c);
16696         this.dataMap = null;
16697         this.fireEvent("columnmoved", this, oldIndex, newIndex);
16698     },
16699
16700     isLocked : function(colIndex){
16701         return this.config[colIndex].locked === true;
16702     },
16703
16704     setLocked : function(colIndex, value, suppressEvent){
16705         if(this.isLocked(colIndex) == value){
16706             return;
16707         }
16708         this.config[colIndex].locked = value;
16709         if(!suppressEvent){
16710             this.fireEvent("columnlockchange", this, colIndex, value);
16711         }
16712     },
16713
16714     getTotalLockedWidth : function(){
16715         var totalWidth = 0;
16716         for(var i = 0; i < this.config.length; i++){
16717             if(this.isLocked(i) && !this.isHidden(i)){
16718                 this.totalWidth += this.getColumnWidth(i);
16719             }
16720         }
16721         return totalWidth;
16722     },
16723
16724     getLockedCount : function(){
16725         for(var i = 0, len = this.config.length; i < len; i++){
16726             if(!this.isLocked(i)){
16727                 return i;
16728             }
16729         }
16730     },
16731
16732     /**
16733      * Returns the number of columns.
16734      * @return {Number}
16735      */
16736     getColumnCount : function(visibleOnly){
16737         if(visibleOnly === true){
16738             var c = 0;
16739             for(var i = 0, len = this.config.length; i < len; i++){
16740                 if(!this.isHidden(i)){
16741                     c++;
16742                 }
16743             }
16744             return c;
16745         }
16746         return this.config.length;
16747     },
16748
16749     /**
16750      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
16751      * @param {Function} fn
16752      * @param {Object} scope (optional)
16753      * @return {Array} result
16754      */
16755     getColumnsBy : function(fn, scope){
16756         var r = [];
16757         for(var i = 0, len = this.config.length; i < len; i++){
16758             var c = this.config[i];
16759             if(fn.call(scope||this, c, i) === true){
16760                 r[r.length] = c;
16761             }
16762         }
16763         return r;
16764     },
16765
16766     /**
16767      * Returns true if the specified column is sortable.
16768      * @param {Number} col The column index
16769      * @return {Boolean}
16770      */
16771     isSortable : function(col){
16772         if(typeof this.config[col].sortable == "undefined"){
16773             return this.defaultSortable;
16774         }
16775         return this.config[col].sortable;
16776     },
16777
16778     /**
16779      * Returns the rendering (formatting) function defined for the column.
16780      * @param {Number} col The column index.
16781      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
16782      */
16783     getRenderer : function(col){
16784         if(!this.config[col].renderer){
16785             return Roo.grid.ColumnModel.defaultRenderer;
16786         }
16787         return this.config[col].renderer;
16788     },
16789
16790     /**
16791      * Sets the rendering (formatting) function for a column.
16792      * @param {Number} col The column index
16793      * @param {Function} fn The function to use to process the cell's raw data
16794      * to return HTML markup for the grid view. The render function is called with
16795      * the following parameters:<ul>
16796      * <li>Data value.</li>
16797      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
16798      * <li>css A CSS style string to apply to the table cell.</li>
16799      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
16800      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
16801      * <li>Row index</li>
16802      * <li>Column index</li>
16803      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
16804      */
16805     setRenderer : function(col, fn){
16806         this.config[col].renderer = fn;
16807     },
16808
16809     /**
16810      * Returns the width for the specified column.
16811      * @param {Number} col The column index
16812      * @return {Number}
16813      */
16814     getColumnWidth : function(col){
16815         return this.config[col].width * 1 || this.defaultWidth;
16816     },
16817
16818     /**
16819      * Sets the width for a column.
16820      * @param {Number} col The column index
16821      * @param {Number} width The new width
16822      */
16823     setColumnWidth : function(col, width, suppressEvent){
16824         this.config[col].width = width;
16825         this.totalWidth = null;
16826         if(!suppressEvent){
16827              this.fireEvent("widthchange", this, col, width);
16828         }
16829     },
16830
16831     /**
16832      * Returns the total width of all columns.
16833      * @param {Boolean} includeHidden True to include hidden column widths
16834      * @return {Number}
16835      */
16836     getTotalWidth : function(includeHidden){
16837         if(!this.totalWidth){
16838             this.totalWidth = 0;
16839             for(var i = 0, len = this.config.length; i < len; i++){
16840                 if(includeHidden || !this.isHidden(i)){
16841                     this.totalWidth += this.getColumnWidth(i);
16842                 }
16843             }
16844         }
16845         return this.totalWidth;
16846     },
16847
16848     /**
16849      * Returns the header for the specified column.
16850      * @param {Number} col The column index
16851      * @return {String}
16852      */
16853     getColumnHeader : function(col){
16854         return this.config[col].header;
16855     },
16856
16857     /**
16858      * Sets the header for a column.
16859      * @param {Number} col The column index
16860      * @param {String} header The new header
16861      */
16862     setColumnHeader : function(col, header){
16863         this.config[col].header = header;
16864         this.fireEvent("headerchange", this, col, header);
16865     },
16866
16867     /**
16868      * Returns the tooltip for the specified column.
16869      * @param {Number} col The column index
16870      * @return {String}
16871      */
16872     getColumnTooltip : function(col){
16873             return this.config[col].tooltip;
16874     },
16875     /**
16876      * Sets the tooltip for a column.
16877      * @param {Number} col The column index
16878      * @param {String} tooltip The new tooltip
16879      */
16880     setColumnTooltip : function(col, tooltip){
16881             this.config[col].tooltip = tooltip;
16882     },
16883
16884     /**
16885      * Returns the dataIndex for the specified column.
16886      * @param {Number} col The column index
16887      * @return {Number}
16888      */
16889     getDataIndex : function(col){
16890         return this.config[col].dataIndex;
16891     },
16892
16893     /**
16894      * Sets the dataIndex for a column.
16895      * @param {Number} col The column index
16896      * @param {Number} dataIndex The new dataIndex
16897      */
16898     setDataIndex : function(col, dataIndex){
16899         this.config[col].dataIndex = dataIndex;
16900     },
16901
16902     
16903     
16904     /**
16905      * Returns true if the cell is editable.
16906      * @param {Number} colIndex The column index
16907      * @param {Number} rowIndex The row index
16908      * @return {Boolean}
16909      */
16910     isCellEditable : function(colIndex, rowIndex){
16911         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16912     },
16913
16914     /**
16915      * Returns the editor defined for the cell/column.
16916      * return false or null to disable editing.
16917      * @param {Number} colIndex The column index
16918      * @param {Number} rowIndex The row index
16919      * @return {Object}
16920      */
16921     getCellEditor : function(colIndex, rowIndex){
16922         return this.config[colIndex].editor;
16923     },
16924
16925     /**
16926      * Sets if a column is editable.
16927      * @param {Number} col The column index
16928      * @param {Boolean} editable True if the column is editable
16929      */
16930     setEditable : function(col, editable){
16931         this.config[col].editable = editable;
16932     },
16933
16934
16935     /**
16936      * Returns true if the column is hidden.
16937      * @param {Number} colIndex The column index
16938      * @return {Boolean}
16939      */
16940     isHidden : function(colIndex){
16941         return this.config[colIndex].hidden;
16942     },
16943
16944
16945     /**
16946      * Returns true if the column width cannot be changed
16947      */
16948     isFixed : function(colIndex){
16949         return this.config[colIndex].fixed;
16950     },
16951
16952     /**
16953      * Returns true if the column can be resized
16954      * @return {Boolean}
16955      */
16956     isResizable : function(colIndex){
16957         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16958     },
16959     /**
16960      * Sets if a column is hidden.
16961      * @param {Number} colIndex The column index
16962      * @param {Boolean} hidden True if the column is hidden
16963      */
16964     setHidden : function(colIndex, hidden){
16965         this.config[colIndex].hidden = hidden;
16966         this.totalWidth = null;
16967         this.fireEvent("hiddenchange", this, colIndex, hidden);
16968     },
16969
16970     /**
16971      * Sets the editor for a column.
16972      * @param {Number} col The column index
16973      * @param {Object} editor The editor object
16974      */
16975     setEditor : function(col, editor){
16976         this.config[col].editor = editor;
16977     }
16978 });
16979
16980 Roo.grid.ColumnModel.defaultRenderer = function(value){
16981         if(typeof value == "string" && value.length < 1){
16982             return "&#160;";
16983         }
16984         return value;
16985 };
16986
16987 // Alias for backwards compatibility
16988 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
16989
16990 /**
16991  * @class Roo.bootstrap.Table.AbstractSelectionModel
16992  * @extends Roo.util.Observable
16993  * Abstract base class for grid SelectionModels.  It provides the interface that should be
16994  * implemented by descendant classes.  This class should not be directly instantiated.
16995  * @constructor
16996  */
16997 Roo.bootstrap.Table.AbstractSelectionModel = function(){
16998     this.locked = false;
16999     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
17000 };
17001
17002
17003 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
17004     /** @ignore Called by the grid automatically. Do not call directly. */
17005     init : function(grid){
17006         this.grid = grid;
17007         this.initEvents();
17008     },
17009
17010     /**
17011      * Locks the selections.
17012      */
17013     lock : function(){
17014         this.locked = true;
17015     },
17016
17017     /**
17018      * Unlocks the selections.
17019      */
17020     unlock : function(){
17021         this.locked = false;
17022     },
17023
17024     /**
17025      * Returns true if the selections are locked.
17026      * @return {Boolean}
17027      */
17028     isLocked : function(){
17029         return this.locked;
17030     }
17031 });
17032 /**
17033  * @extends Roo.bootstrap.Table.AbstractSelectionModel
17034  * @class Roo.bootstrap.Table.RowSelectionModel
17035  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
17036  * It supports multiple selections and keyboard selection/navigation. 
17037  * @constructor
17038  * @param {Object} config
17039  */
17040
17041 Roo.bootstrap.Table.RowSelectionModel = function(config){
17042     Roo.apply(this, config);
17043     this.selections = new Roo.util.MixedCollection(false, function(o){
17044         return o.id;
17045     });
17046
17047     this.last = false;
17048     this.lastActive = false;
17049
17050     this.addEvents({
17051         /**
17052              * @event selectionchange
17053              * Fires when the selection changes
17054              * @param {SelectionModel} this
17055              */
17056             "selectionchange" : true,
17057         /**
17058              * @event afterselectionchange
17059              * Fires after the selection changes (eg. by key press or clicking)
17060              * @param {SelectionModel} this
17061              */
17062             "afterselectionchange" : true,
17063         /**
17064              * @event beforerowselect
17065              * Fires when a row is selected being selected, return false to cancel.
17066              * @param {SelectionModel} this
17067              * @param {Number} rowIndex The selected index
17068              * @param {Boolean} keepExisting False if other selections will be cleared
17069              */
17070             "beforerowselect" : true,
17071         /**
17072              * @event rowselect
17073              * Fires when a row is selected.
17074              * @param {SelectionModel} this
17075              * @param {Number} rowIndex The selected index
17076              * @param {Roo.data.Record} r The record
17077              */
17078             "rowselect" : true,
17079         /**
17080              * @event rowdeselect
17081              * Fires when a row is deselected.
17082              * @param {SelectionModel} this
17083              * @param {Number} rowIndex The selected index
17084              */
17085         "rowdeselect" : true
17086     });
17087     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
17088     this.locked = false;
17089 };
17090
17091 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
17092     /**
17093      * @cfg {Boolean} singleSelect
17094      * True to allow selection of only one row at a time (defaults to false)
17095      */
17096     singleSelect : false,
17097
17098     // private
17099     initEvents : function(){
17100
17101         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
17102             this.grid.on("mousedown", this.handleMouseDown, this);
17103         }else{ // allow click to work like normal
17104             this.grid.on("rowclick", this.handleDragableRowClick, this);
17105         }
17106
17107         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
17108             "up" : function(e){
17109                 if(!e.shiftKey){
17110                     this.selectPrevious(e.shiftKey);
17111                 }else if(this.last !== false && this.lastActive !== false){
17112                     var last = this.last;
17113                     this.selectRange(this.last,  this.lastActive-1);
17114                     this.grid.getView().focusRow(this.lastActive);
17115                     if(last !== false){
17116                         this.last = last;
17117                     }
17118                 }else{
17119                     this.selectFirstRow();
17120                 }
17121                 this.fireEvent("afterselectionchange", this);
17122             },
17123             "down" : function(e){
17124                 if(!e.shiftKey){
17125                     this.selectNext(e.shiftKey);
17126                 }else if(this.last !== false && this.lastActive !== false){
17127                     var last = this.last;
17128                     this.selectRange(this.last,  this.lastActive+1);
17129                     this.grid.getView().focusRow(this.lastActive);
17130                     if(last !== false){
17131                         this.last = last;
17132                     }
17133                 }else{
17134                     this.selectFirstRow();
17135                 }
17136                 this.fireEvent("afterselectionchange", this);
17137             },
17138             scope: this
17139         });
17140
17141         var view = this.grid.view;
17142         view.on("refresh", this.onRefresh, this);
17143         view.on("rowupdated", this.onRowUpdated, this);
17144         view.on("rowremoved", this.onRemove, this);
17145     },
17146
17147     // private
17148     onRefresh : function(){
17149         var ds = this.grid.dataSource, i, v = this.grid.view;
17150         var s = this.selections;
17151         s.each(function(r){
17152             if((i = ds.indexOfId(r.id)) != -1){
17153                 v.onRowSelect(i);
17154             }else{
17155                 s.remove(r);
17156             }
17157         });
17158     },
17159
17160     // private
17161     onRemove : function(v, index, r){
17162         this.selections.remove(r);
17163     },
17164
17165     // private
17166     onRowUpdated : function(v, index, r){
17167         if(this.isSelected(r)){
17168             v.onRowSelect(index);
17169         }
17170     },
17171
17172     /**
17173      * Select records.
17174      * @param {Array} records The records to select
17175      * @param {Boolean} keepExisting (optional) True to keep existing selections
17176      */
17177     selectRecords : function(records, keepExisting){
17178         if(!keepExisting){
17179             this.clearSelections();
17180         }
17181         var ds = this.grid.dataSource;
17182         for(var i = 0, len = records.length; i < len; i++){
17183             this.selectRow(ds.indexOf(records[i]), true);
17184         }
17185     },
17186
17187     /**
17188      * Gets the number of selected rows.
17189      * @return {Number}
17190      */
17191     getCount : function(){
17192         return this.selections.length;
17193     },
17194
17195     /**
17196      * Selects the first row in the grid.
17197      */
17198     selectFirstRow : function(){
17199         this.selectRow(0);
17200     },
17201
17202     /**
17203      * Select the last row.
17204      * @param {Boolean} keepExisting (optional) True to keep existing selections
17205      */
17206     selectLastRow : function(keepExisting){
17207         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
17208     },
17209
17210     /**
17211      * Selects the row immediately following the last selected row.
17212      * @param {Boolean} keepExisting (optional) True to keep existing selections
17213      */
17214     selectNext : function(keepExisting){
17215         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
17216             this.selectRow(this.last+1, keepExisting);
17217             this.grid.getView().focusRow(this.last);
17218         }
17219     },
17220
17221     /**
17222      * Selects the row that precedes the last selected row.
17223      * @param {Boolean} keepExisting (optional) True to keep existing selections
17224      */
17225     selectPrevious : function(keepExisting){
17226         if(this.last){
17227             this.selectRow(this.last-1, keepExisting);
17228             this.grid.getView().focusRow(this.last);
17229         }
17230     },
17231
17232     /**
17233      * Returns the selected records
17234      * @return {Array} Array of selected records
17235      */
17236     getSelections : function(){
17237         return [].concat(this.selections.items);
17238     },
17239
17240     /**
17241      * Returns the first selected record.
17242      * @return {Record}
17243      */
17244     getSelected : function(){
17245         return this.selections.itemAt(0);
17246     },
17247
17248
17249     /**
17250      * Clears all selections.
17251      */
17252     clearSelections : function(fast){
17253         if(this.locked) return;
17254         if(fast !== true){
17255             var ds = this.grid.dataSource;
17256             var s = this.selections;
17257             s.each(function(r){
17258                 this.deselectRow(ds.indexOfId(r.id));
17259             }, this);
17260             s.clear();
17261         }else{
17262             this.selections.clear();
17263         }
17264         this.last = false;
17265     },
17266
17267
17268     /**
17269      * Selects all rows.
17270      */
17271     selectAll : function(){
17272         if(this.locked) return;
17273         this.selections.clear();
17274         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
17275             this.selectRow(i, true);
17276         }
17277     },
17278
17279     /**
17280      * Returns True if there is a selection.
17281      * @return {Boolean}
17282      */
17283     hasSelection : function(){
17284         return this.selections.length > 0;
17285     },
17286
17287     /**
17288      * Returns True if the specified row is selected.
17289      * @param {Number/Record} record The record or index of the record to check
17290      * @return {Boolean}
17291      */
17292     isSelected : function(index){
17293         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
17294         return (r && this.selections.key(r.id) ? true : false);
17295     },
17296
17297     /**
17298      * Returns True if the specified record id is selected.
17299      * @param {String} id The id of record to check
17300      * @return {Boolean}
17301      */
17302     isIdSelected : function(id){
17303         return (this.selections.key(id) ? true : false);
17304     },
17305
17306     // private
17307     handleMouseDown : function(e, t){
17308         var view = this.grid.getView(), rowIndex;
17309         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
17310             return;
17311         };
17312         if(e.shiftKey && this.last !== false){
17313             var last = this.last;
17314             this.selectRange(last, rowIndex, e.ctrlKey);
17315             this.last = last; // reset the last
17316             view.focusRow(rowIndex);
17317         }else{
17318             var isSelected = this.isSelected(rowIndex);
17319             if(e.button !== 0 && isSelected){
17320                 view.focusRow(rowIndex);
17321             }else if(e.ctrlKey && isSelected){
17322                 this.deselectRow(rowIndex);
17323             }else if(!isSelected){
17324                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
17325                 view.focusRow(rowIndex);
17326             }
17327         }
17328         this.fireEvent("afterselectionchange", this);
17329     },
17330     // private
17331     handleDragableRowClick :  function(grid, rowIndex, e) 
17332     {
17333         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
17334             this.selectRow(rowIndex, false);
17335             grid.view.focusRow(rowIndex);
17336              this.fireEvent("afterselectionchange", this);
17337         }
17338     },
17339     
17340     /**
17341      * Selects multiple rows.
17342      * @param {Array} rows Array of the indexes of the row to select
17343      * @param {Boolean} keepExisting (optional) True to keep existing selections
17344      */
17345     selectRows : function(rows, keepExisting){
17346         if(!keepExisting){
17347             this.clearSelections();
17348         }
17349         for(var i = 0, len = rows.length; i < len; i++){
17350             this.selectRow(rows[i], true);
17351         }
17352     },
17353
17354     /**
17355      * Selects a range of rows. All rows in between startRow and endRow are also selected.
17356      * @param {Number} startRow The index of the first row in the range
17357      * @param {Number} endRow The index of the last row in the range
17358      * @param {Boolean} keepExisting (optional) True to retain existing selections
17359      */
17360     selectRange : function(startRow, endRow, keepExisting){
17361         if(this.locked) return;
17362         if(!keepExisting){
17363             this.clearSelections();
17364         }
17365         if(startRow <= endRow){
17366             for(var i = startRow; i <= endRow; i++){
17367                 this.selectRow(i, true);
17368             }
17369         }else{
17370             for(var i = startRow; i >= endRow; i--){
17371                 this.selectRow(i, true);
17372             }
17373         }
17374     },
17375
17376     /**
17377      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
17378      * @param {Number} startRow The index of the first row in the range
17379      * @param {Number} endRow The index of the last row in the range
17380      */
17381     deselectRange : function(startRow, endRow, preventViewNotify){
17382         if(this.locked) return;
17383         for(var i = startRow; i <= endRow; i++){
17384             this.deselectRow(i, preventViewNotify);
17385         }
17386     },
17387
17388     /**
17389      * Selects a row.
17390      * @param {Number} row The index of the row to select
17391      * @param {Boolean} keepExisting (optional) True to keep existing selections
17392      */
17393     selectRow : function(index, keepExisting, preventViewNotify){
17394         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
17395         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
17396             if(!keepExisting || this.singleSelect){
17397                 this.clearSelections();
17398             }
17399             var r = this.grid.dataSource.getAt(index);
17400             this.selections.add(r);
17401             this.last = this.lastActive = index;
17402             if(!preventViewNotify){
17403                 this.grid.getView().onRowSelect(index);
17404             }
17405             this.fireEvent("rowselect", this, index, r);
17406             this.fireEvent("selectionchange", this);
17407         }
17408     },
17409
17410     /**
17411      * Deselects a row.
17412      * @param {Number} row The index of the row to deselect
17413      */
17414     deselectRow : function(index, preventViewNotify){
17415         if(this.locked) return;
17416         if(this.last == index){
17417             this.last = false;
17418         }
17419         if(this.lastActive == index){
17420             this.lastActive = false;
17421         }
17422         var r = this.grid.dataSource.getAt(index);
17423         this.selections.remove(r);
17424         if(!preventViewNotify){
17425             this.grid.getView().onRowDeselect(index);
17426         }
17427         this.fireEvent("rowdeselect", this, index);
17428         this.fireEvent("selectionchange", this);
17429     },
17430
17431     // private
17432     restoreLast : function(){
17433         if(this._last){
17434             this.last = this._last;
17435         }
17436     },
17437
17438     // private
17439     acceptsNav : function(row, col, cm){
17440         return !cm.isHidden(col) && cm.isCellEditable(col, row);
17441     },
17442
17443     // private
17444     onEditorKey : function(field, e){
17445         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
17446         if(k == e.TAB){
17447             e.stopEvent();
17448             ed.completeEdit();
17449             if(e.shiftKey){
17450                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
17451             }else{
17452                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
17453             }
17454         }else if(k == e.ENTER && !e.ctrlKey){
17455             e.stopEvent();
17456             ed.completeEdit();
17457             if(e.shiftKey){
17458                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
17459             }else{
17460                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
17461             }
17462         }else if(k == e.ESC){
17463             ed.cancelEdit();
17464         }
17465         if(newCell){
17466             g.startEditing(newCell[0], newCell[1]);
17467         }
17468     }
17469 });/*
17470  * Based on:
17471  * Ext JS Library 1.1.1
17472  * Copyright(c) 2006-2007, Ext JS, LLC.
17473  *
17474  * Originally Released Under LGPL - original licence link has changed is not relivant.
17475  *
17476  * Fork - LGPL
17477  * <script type="text/javascript">
17478  */
17479  
17480 /**
17481  * @class Roo.bootstrap.PagingToolbar
17482  * @extends Roo.Row
17483  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
17484  * @constructor
17485  * Create a new PagingToolbar
17486  * @param {Object} config The config object
17487  */
17488 Roo.bootstrap.PagingToolbar = function(config)
17489 {
17490     // old args format still supported... - xtype is prefered..
17491         // created from xtype...
17492     var ds = config.dataSource;
17493     var items = [];
17494     if (config.items) {
17495         items = config.items;
17496         config.items = [];
17497     }
17498     
17499     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
17500     this.ds = ds;
17501     this.cursor = 0;
17502     if (ds) { 
17503         this.bind(ds);
17504     }
17505     
17506     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
17507     
17508     // supprot items array.
17509     
17510     Roo.each(items, function(e) {
17511         this.add(Roo.factory(e));
17512     },this);
17513     
17514     
17515     
17516     
17517     
17518 };
17519
17520 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
17521     /**
17522      * @cfg {Roo.data.Store} dataSource
17523      * The underlying data store providing the paged data
17524      */
17525     /**
17526      * @cfg {String/HTMLElement/Element} container
17527      * container The id or element that will contain the toolbar
17528      */
17529     /**
17530      * @cfg {Boolean} displayInfo
17531      * True to display the displayMsg (defaults to false)
17532      */
17533     /**
17534      * @cfg {Number} pageSize
17535      * The number of records to display per page (defaults to 20)
17536      */
17537     pageSize: 20,
17538     /**
17539      * @cfg {String} displayMsg
17540      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
17541      */
17542     displayMsg : 'Displaying {0} - {1} of {2}',
17543     /**
17544      * @cfg {String} emptyMsg
17545      * The message to display when no records are found (defaults to "No data to display")
17546      */
17547     emptyMsg : 'No data to display',
17548     /**
17549      * Customizable piece of the default paging text (defaults to "Page")
17550      * @type String
17551      */
17552     beforePageText : "Page",
17553     /**
17554      * Customizable piece of the default paging text (defaults to "of %0")
17555      * @type String
17556      */
17557     afterPageText : "of {0}",
17558     /**
17559      * Customizable piece of the default paging text (defaults to "First Page")
17560      * @type String
17561      */
17562     firstText : "First Page",
17563     /**
17564      * Customizable piece of the default paging text (defaults to "Previous Page")
17565      * @type String
17566      */
17567     prevText : "Previous Page",
17568     /**
17569      * Customizable piece of the default paging text (defaults to "Next Page")
17570      * @type String
17571      */
17572     nextText : "Next Page",
17573     /**
17574      * Customizable piece of the default paging text (defaults to "Last Page")
17575      * @type String
17576      */
17577     lastText : "Last Page",
17578     /**
17579      * Customizable piece of the default paging text (defaults to "Refresh")
17580      * @type String
17581      */
17582     refreshText : "Refresh",
17583
17584     // private
17585     onRender : function(ct, position) 
17586     {
17587         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
17588         this.navgroup.parentId = this.id;
17589         this.navgroup.onRender(this.el, null);
17590         // add the buttons to the navgroup
17591         
17592         this.first = this.navgroup.addItem({
17593             tooltip: this.firstText,
17594             cls: "prev",
17595             icon : 'fa fa-backward',
17596             disabled: true,
17597             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
17598         });
17599         
17600         this.prev =  this.navgroup.addItem({
17601             tooltip: this.prevText,
17602             cls: "prev",
17603             icon : 'fa fa-step-backward',
17604             disabled: true,
17605             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
17606         });
17607     //this.addSeparator();
17608         
17609         
17610         var field = this.navgroup.addItem( {
17611             tagtype : 'span',
17612             cls : 'x-paging-position',
17613             
17614             html : this.beforePageText  +
17615                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
17616                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
17617          } ); //?? escaped?
17618         
17619         this.field = field.el.select('input', true).first();
17620         this.field.on("keydown", this.onPagingKeydown, this);
17621         this.field.on("focus", function(){this.dom.select();});
17622     
17623     
17624         this.afterTextEl =  field.el.select('.x-paging-after').first();
17625         //this.field.setHeight(18);
17626         //this.addSeparator();
17627         this.next = this.navgroup.addItem({
17628             tooltip: this.nextText,
17629             cls: "next",
17630             html : ' <i class="fa fa-step-forward">',
17631             disabled: true,
17632             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
17633         });
17634         this.last = this.navgroup.addItem({
17635             tooltip: this.lastText,
17636             icon : 'fa fa-forward',
17637             cls: "next",
17638             disabled: true,
17639             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
17640         });
17641     //this.addSeparator();
17642         this.loading = this.navgroup.addItem({
17643             tooltip: this.refreshText,
17644             icon: 'fa fa-refresh',
17645             
17646             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
17647         });
17648
17649         if(this.displayInfo){
17650             var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info' } );
17651             this.displayEl = navel.el.select('a',true).first();
17652         }
17653     
17654     },
17655
17656     // private
17657     updateInfo : function(){
17658         if(this.displayEl){
17659             var count = this.ds.getCount();
17660             var msg = count == 0 ?
17661                 this.emptyMsg :
17662                 String.format(
17663                     this.displayMsg,
17664                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
17665                 );
17666             this.displayEl.update(msg);
17667         }
17668     },
17669
17670     // private
17671     onLoad : function(ds, r, o){
17672        this.cursor = o.params ? o.params.start : 0;
17673        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
17674
17675        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
17676        this.field.dom.value = ap;
17677        this.first.setDisabled(ap == 1);
17678        this.prev.setDisabled(ap == 1);
17679        this.next.setDisabled(ap == ps);
17680        this.last.setDisabled(ap == ps);
17681        this.loading.enable();
17682        this.updateInfo();
17683     },
17684
17685     // private
17686     getPageData : function(){
17687         var total = this.ds.getTotalCount();
17688         return {
17689             total : total,
17690             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
17691             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
17692         };
17693     },
17694
17695     // private
17696     onLoadError : function(){
17697         this.loading.enable();
17698     },
17699
17700     // private
17701     onPagingKeydown : function(e){
17702         var k = e.getKey();
17703         var d = this.getPageData();
17704         if(k == e.RETURN){
17705             var v = this.field.dom.value, pageNum;
17706             if(!v || isNaN(pageNum = parseInt(v, 10))){
17707                 this.field.dom.value = d.activePage;
17708                 return;
17709             }
17710             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
17711             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
17712             e.stopEvent();
17713         }
17714         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))
17715         {
17716           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
17717           this.field.dom.value = pageNum;
17718           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
17719           e.stopEvent();
17720         }
17721         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
17722         {
17723           var v = this.field.dom.value, pageNum; 
17724           var increment = (e.shiftKey) ? 10 : 1;
17725           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
17726             increment *= -1;
17727           if(!v || isNaN(pageNum = parseInt(v, 10))) {
17728             this.field.dom.value = d.activePage;
17729             return;
17730           }
17731           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
17732           {
17733             this.field.dom.value = parseInt(v, 10) + increment;
17734             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
17735             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
17736           }
17737           e.stopEvent();
17738         }
17739     },
17740
17741     // private
17742     beforeLoad : function(){
17743         if(this.loading){
17744             this.loading.disable();
17745         }
17746     },
17747
17748     // private
17749     onClick : function(which){
17750         var ds = this.ds;
17751         if (!ds) {
17752             return;
17753         }
17754         switch(which){
17755             case "first":
17756                 ds.load({params:{start: 0, limit: this.pageSize}});
17757             break;
17758             case "prev":
17759                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
17760             break;
17761             case "next":
17762                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
17763             break;
17764             case "last":
17765                 var total = ds.getTotalCount();
17766                 var extra = total % this.pageSize;
17767                 var lastStart = extra ? (total - extra) : total-this.pageSize;
17768                 ds.load({params:{start: lastStart, limit: this.pageSize}});
17769             break;
17770             case "refresh":
17771                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
17772             break;
17773         }
17774     },
17775
17776     /**
17777      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
17778      * @param {Roo.data.Store} store The data store to unbind
17779      */
17780     unbind : function(ds){
17781         ds.un("beforeload", this.beforeLoad, this);
17782         ds.un("load", this.onLoad, this);
17783         ds.un("loadexception", this.onLoadError, this);
17784         ds.un("remove", this.updateInfo, this);
17785         ds.un("add", this.updateInfo, this);
17786         this.ds = undefined;
17787     },
17788
17789     /**
17790      * Binds the paging toolbar to the specified {@link Roo.data.Store}
17791      * @param {Roo.data.Store} store The data store to bind
17792      */
17793     bind : function(ds){
17794         ds.on("beforeload", this.beforeLoad, this);
17795         ds.on("load", this.onLoad, this);
17796         ds.on("loadexception", this.onLoadError, this);
17797         ds.on("remove", this.updateInfo, this);
17798         ds.on("add", this.updateInfo, this);
17799         this.ds = ds;
17800     }
17801 });/*
17802  * - LGPL
17803  *
17804  * element
17805  * 
17806  */
17807
17808 /**
17809  * @class Roo.bootstrap.MessageBar
17810  * @extends Roo.bootstrap.Component
17811  * Bootstrap MessageBar class
17812  * @cfg {String} html contents of the MessageBar
17813  * @cfg {String} weight (info | success | warning | danger) default info
17814  * @cfg {String} beforeClass insert the bar before the given class
17815  * @cfg {Boolean} closable (true | false) default false
17816  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
17817  * 
17818  * @constructor
17819  * Create a new Element
17820  * @param {Object} config The config object
17821  */
17822
17823 Roo.bootstrap.MessageBar = function(config){
17824     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
17825 };
17826
17827 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
17828     
17829     html: '',
17830     weight: 'info',
17831     closable: false,
17832     fixed: false,
17833     beforeClass: 'bootstrap-sticky-wrap',
17834     
17835     getAutoCreate : function(){
17836         
17837         var cfg = {
17838             tag: 'div',
17839             cls: 'alert alert-dismissable alert-' + this.weight,
17840             cn: [
17841                 {
17842                     tag: 'span',
17843                     cls: 'message',
17844                     html: this.html || ''
17845                 }
17846             ]
17847         }
17848         
17849         if(this.fixed){
17850             cfg.cls += ' alert-messages-fixed';
17851         }
17852         
17853         if(this.closable){
17854             cfg.cn.push({
17855                 tag: 'button',
17856                 cls: 'close',
17857                 html: 'x'
17858             });
17859         }
17860         
17861         return cfg;
17862     },
17863     
17864     onRender : function(ct, position)
17865     {
17866         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17867         
17868         if(!this.el){
17869             var cfg = Roo.apply({},  this.getAutoCreate());
17870             cfg.id = Roo.id();
17871             
17872             if (this.cls) {
17873                 cfg.cls += ' ' + this.cls;
17874             }
17875             if (this.style) {
17876                 cfg.style = this.style;
17877             }
17878             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
17879             
17880             this.el.setVisibilityMode(Roo.Element.DISPLAY);
17881         }
17882         
17883         this.el.select('>button.close').on('click', this.hide, this);
17884         
17885     },
17886     
17887     show : function()
17888     {
17889         if (!this.rendered) {
17890             this.render();
17891         }
17892         
17893         this.el.show();
17894         
17895         this.fireEvent('show', this);
17896         
17897     },
17898     
17899     hide : function()
17900     {
17901         if (!this.rendered) {
17902             this.render();
17903         }
17904         
17905         this.el.hide();
17906         
17907         this.fireEvent('hide', this);
17908     },
17909     
17910     update : function()
17911     {
17912 //        var e = this.el.dom.firstChild;
17913 //        
17914 //        if(this.closable){
17915 //            e = e.nextSibling;
17916 //        }
17917 //        
17918 //        e.data = this.html || '';
17919
17920         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
17921     }
17922    
17923 });
17924
17925  
17926
17927