Roo/grid/GridView.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         this.initEvents();
140         
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165            
166         cn.parentType = this.xtype; //??
167         cn.parentId = this.id;
168         
169         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
170         if (typeof(cn.container_method) == 'string') {
171             cntr = cn.container_method;
172         }
173         
174         
175         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
176         
177         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
178         
179         var build_from_html =  Roo.XComponent.build_from_html;
180           
181         var is_body  = (tree.xtype == 'Body') ;
182           
183         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
184           
185         var self_cntr_el = Roo.get(this[cntr](false));
186         
187         // do not try and build conditional elements 
188         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
189             return false;
190         }
191         
192         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
193             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
194                 return this.addxtypeChild(tree,cntr);
195             }
196             
197             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
198                 
199             if(echild){
200                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
201             }
202             
203             Roo.log('skipping render');
204             return cn;
205             
206         }
207         
208         var ret = false;
209         if (!build_from_html) {
210             return false;
211         }
212         
213         // this i think handles overlaying multiple children of the same type
214         // with the sam eelement.. - which might be buggy..
215         while (true) {
216             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
217             
218             if (!echild) {
219                 break;
220             }
221             
222             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
223                 break;
224             }
225             
226             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
227         }
228         return ret;
229     },
230     
231     addxtypeChild : function (tree, cntr)
232     {
233         Roo.debug && Roo.log('addxtypeChild:' + cntr);
234         var cn = this;
235         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
236         
237         
238         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
239                     (typeof(tree['flexy:foreach']) != 'undefined');
240           
241         
242         
243          skip_children = false;
244         // render the element if it's not BODY.
245         if (tree.xtype != 'Body') {
246            
247             cn = Roo.factory(tree);
248            
249             cn.parentType = this.xtype; //??
250             cn.parentId = this.id;
251             
252             var build_from_html =  Roo.XComponent.build_from_html;
253             
254             
255             // does the container contain child eleemnts with 'xtype' attributes.
256             // that match this xtype..
257             // note - when we render we create these as well..
258             // so we should check to see if body has xtype set.
259             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
260                
261                 var self_cntr_el = Roo.get(this[cntr](false));
262                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
263                 if (echild) { 
264                     //Roo.log(Roo.XComponent.build_from_html);
265                     //Roo.log("got echild:");
266                     //Roo.log(echild);
267                 }
268                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
269                 // and are not displayed -this causes this to use up the wrong element when matching.
270                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
271                 
272                 
273                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
274                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
275                   
276                   
277                   
278                     cn.el = echild;
279                   //  Roo.log("GOT");
280                     //echild.dom.removeAttribute('xtype');
281                 } else {
282                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
283                     Roo.debug && Roo.log(self_cntr_el);
284                     Roo.debug && Roo.log(echild);
285                     Roo.debug && Roo.log(cn);
286                 }
287             }
288            
289             
290            
291             // if object has flexy:if - then it may or may not be rendered.
292             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
293                 // skip a flexy if element.
294                 Roo.debug && Roo.log('skipping render');
295                 Roo.debug && Roo.log(tree);
296                 if (!cn.el) {
297                     Roo.debug && Roo.log('skipping all children');
298                     skip_children = true;
299                 }
300                 
301              } else {
302                  
303                 // actually if flexy:foreach is found, we really want to create 
304                 // multiple copies here...
305                 //Roo.log('render');
306                 //Roo.log(this[cntr]());
307                 cn.render(this[cntr](true));
308              }
309             // then add the element..
310         }
311         
312         
313         // handle the kids..
314         
315         var nitems = [];
316         /*
317         if (typeof (tree.menu) != 'undefined') {
318             tree.menu.parentType = cn.xtype;
319             tree.menu.triggerEl = cn.el;
320             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
321             
322         }
323         */
324         if (!tree.items || !tree.items.length) {
325             cn.items = nitems;
326             return cn;
327         }
328         var items = tree.items;
329         delete tree.items;
330         
331         //Roo.log(items.length);
332             // add the items..
333         if (!skip_children) {    
334             for(var i =0;i < items.length;i++) {
335                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
336             }
337         }
338         
339         cn.items = nitems;
340         
341         this.fireEvent('childrenrendered', this);
342         
343         return cn;
344     },
345     /**
346      * Show a component - removes 'hidden' class
347      */
348     show : function()
349     {
350         if (this.el) {
351             this.el.removeClass('hidden');
352         }
353     },
354     /**
355      * Hide a component - adds 'hidden' class
356      */
357     hide: function()
358     {
359         if (this.el && !this.el.hasClass('hidden')) {
360             this.el.addClass('hidden');
361         }
362         
363     }
364 });
365
366  /*
367  * - LGPL
368  *
369  * Body
370  * 
371  */
372
373 /**
374  * @class Roo.bootstrap.Body
375  * @extends Roo.bootstrap.Component
376  * Bootstrap Body class
377  * 
378  * @constructor
379  * Create a new body
380  * @param {Object} config The config object
381  */
382
383 Roo.bootstrap.Body = function(config){
384     Roo.bootstrap.Body.superclass.constructor.call(this, config);
385     this.el = Roo.get(document.body);
386     if (this.cls && this.cls.length) {
387         Roo.get(document.body).addClass(this.cls);
388     }
389 };
390
391 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
392       
393         autoCreate : {
394         cls: 'container'
395     },
396     onRender : function(ct, position)
397     {
398        /* Roo.log("Roo.bootstrap.Body - onRender");
399         if (this.cls && this.cls.length) {
400             Roo.get(document.body).addClass(this.cls);
401         }
402         // style??? xttr???
403         */
404     }
405     
406     
407  
408    
409 });
410
411  /*
412  * - LGPL
413  *
414  * button group
415  * 
416  */
417
418
419 /**
420  * @class Roo.bootstrap.ButtonGroup
421  * @extends Roo.bootstrap.Component
422  * Bootstrap ButtonGroup class
423  * @cfg {String} size lg | sm | xs (default empty normal)
424  * @cfg {String} align vertical | justified  (default none)
425  * @cfg {String} direction up | down (default down)
426  * @cfg {Boolean} toolbar false | true
427  * @cfg {Boolean} btn true | false
428  * 
429  * 
430  * @constructor
431  * Create a new Input
432  * @param {Object} config The config object
433  */
434
435 Roo.bootstrap.ButtonGroup = function(config){
436     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
437 };
438
439 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
440     
441     size: '',
442     align: '',
443     direction: '',
444     toolbar: false,
445     btn: true,
446
447     getAutoCreate : function(){
448         var cfg = {
449             cls: 'btn-group',
450             html : null
451         };
452         
453         cfg.html = this.html || cfg.html;
454         
455         if (this.toolbar) {
456             cfg = {
457                 cls: 'btn-toolbar',
458                 html: null
459             };
460             
461             return cfg;
462         }
463         
464         if (['vertical','justified'].indexOf(this.align)!==-1) {
465             cfg.cls = 'btn-group-' + this.align;
466             
467             if (this.align == 'justified') {
468                 console.log(this.items);
469             }
470         }
471         
472         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
473             cfg.cls += ' btn-group-' + this.size;
474         }
475         
476         if (this.direction == 'up') {
477             cfg.cls += ' dropup' ;
478         }
479         
480         return cfg;
481     }
482    
483 });
484
485  /*
486  * - LGPL
487  *
488  * button
489  * 
490  */
491
492 /**
493  * @class Roo.bootstrap.Button
494  * @extends Roo.bootstrap.Component
495  * Bootstrap Button class
496  * @cfg {String} html The button content
497  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
498  * @cfg {String} size ( lg | sm | xs)
499  * @cfg {String} tag ( a | input | submit)
500  * @cfg {String} href empty or href
501  * @cfg {Boolean} disabled default false;
502  * @cfg {Boolean} isClose default false;
503  * @cfg {String} glyphicon (| 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)
504  * @cfg {String} badge text for badge
505  * @cfg {String} theme default 
506  * @cfg {Boolean} inverse 
507  * @cfg {Boolean} toggle 
508  * @cfg {String} ontext text for on toggle state
509  * @cfg {String} offtext text for off toggle state
510  * @cfg {Boolean} defaulton 
511  * @cfg {Boolean} preventDefault  default true
512  * @cfg {Boolean} removeClass remove the standard class..
513  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
514  * 
515  * @constructor
516  * Create a new button
517  * @param {Object} config The config object
518  */
519
520
521 Roo.bootstrap.Button = function(config){
522     Roo.bootstrap.Button.superclass.constructor.call(this, config);
523     this.addEvents({
524         // raw events
525         /**
526          * @event click
527          * When a butotn is pressed
528          * @param {Roo.bootstrap.Button} this
529          * @param {Roo.EventObject} e
530          */
531         "click" : true,
532          /**
533          * @event toggle
534          * After the button has been toggles
535          * @param {Roo.EventObject} e
536          * @param {boolean} pressed (also available as button.pressed)
537          */
538         "toggle" : true
539     });
540 };
541
542 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
543     html: false,
544     active: false,
545     weight: '',
546     size: '',
547     tag: 'button',
548     href: '',
549     disabled: false,
550     isClose: false,
551     glyphicon: '',
552     badge: '',
553     theme: 'default',
554     inverse: false,
555     
556     toggle: false,
557     ontext: 'ON',
558     offtext: 'OFF',
559     defaulton: true,
560     preventDefault: true,
561     removeClass: false,
562     name: false,
563     target: false,
564     
565     
566     pressed : null,
567      
568     
569     getAutoCreate : function(){
570         
571         var cfg = {
572             tag : 'button',
573             cls : 'roo-button',
574             html: ''
575         };
576         
577         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
578             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
579             this.tag = 'button';
580         } else {
581             cfg.tag = this.tag;
582         }
583         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
584         
585         if (this.toggle == true) {
586             cfg={
587                 tag: 'div',
588                 cls: 'slider-frame roo-button',
589                 cn: [
590                     {
591                         tag: 'span',
592                         'data-on-text':'ON',
593                         'data-off-text':'OFF',
594                         cls: 'slider-button',
595                         html: this.offtext
596                     }
597                 ]
598             };
599             
600             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
601                 cfg.cls += ' '+this.weight;
602             }
603             
604             return cfg;
605         }
606         
607         if (this.isClose) {
608             cfg.cls += ' close';
609             
610             cfg["aria-hidden"] = true;
611             
612             cfg.html = "&times;";
613             
614             return cfg;
615         }
616         
617          
618         if (this.theme==='default') {
619             cfg.cls = 'btn roo-button';
620             
621             //if (this.parentType != 'Navbar') {
622             this.weight = this.weight.length ?  this.weight : 'default';
623             //}
624             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
625                 
626                 cfg.cls += ' btn-' + this.weight;
627             }
628         } else if (this.theme==='glow') {
629             
630             cfg.tag = 'a';
631             cfg.cls = 'btn-glow roo-button';
632             
633             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
634                 
635                 cfg.cls += ' ' + this.weight;
636             }
637         }
638    
639         
640         if (this.inverse) {
641             this.cls += ' inverse';
642         }
643         
644         
645         if (this.active) {
646             cfg.cls += ' active';
647         }
648         
649         if (this.disabled) {
650             cfg.disabled = 'disabled';
651         }
652         
653         if (this.items) {
654             Roo.log('changing to ul' );
655             cfg.tag = 'ul';
656             this.glyphicon = 'caret';
657         }
658         
659         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
660          
661         //gsRoo.log(this.parentType);
662         if (this.parentType === 'Navbar' && !this.parent().bar) {
663             Roo.log('changing to li?');
664             
665             cfg.tag = 'li';
666             
667             cfg.cls = '';
668             cfg.cn =  [{
669                 tag : 'a',
670                 cls : 'roo-button',
671                 html : this.html,
672                 href : this.href || '#'
673             }];
674             if (this.menu) {
675                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
676                 cfg.cls += ' dropdown';
677             }   
678             
679             delete cfg.html;
680             
681         }
682         
683        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
684         
685         if (this.glyphicon) {
686             cfg.html = ' ' + cfg.html;
687             
688             cfg.cn = [
689                 {
690                     tag: 'span',
691                     cls: 'glyphicon glyphicon-' + this.glyphicon
692                 }
693             ];
694         }
695         
696         if (this.badge) {
697             cfg.html += ' ';
698             
699             cfg.tag = 'a';
700             
701 //            cfg.cls='btn roo-button';
702             
703             cfg.href=this.href;
704             
705             var value = cfg.html;
706             
707             if(this.glyphicon){
708                 value = {
709                             tag: 'span',
710                             cls: 'glyphicon glyphicon-' + this.glyphicon,
711                             html: this.html
712                         };
713                 
714             }
715             
716             cfg.cn = [
717                 value,
718                 {
719                     tag: 'span',
720                     cls: 'badge',
721                     html: this.badge
722                 }
723             ];
724             
725             cfg.html='';
726         }
727         
728         if (this.menu) {
729             cfg.cls += ' dropdown';
730             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
731         }
732         
733         if (cfg.tag !== 'a' && this.href !== '') {
734             throw "Tag must be a to set href.";
735         } else if (this.href.length > 0) {
736             cfg.href = this.href;
737         }
738         
739         if(this.removeClass){
740             cfg.cls = '';
741         }
742         
743         if(this.target){
744             cfg.target = this.target;
745         }
746         
747         return cfg;
748     },
749     initEvents: function() {
750        // Roo.log('init events?');
751 //        Roo.log(this.el.dom);
752         // add the menu...
753         
754         if (typeof (this.menu) != 'undefined') {
755             this.menu.parentType = this.xtype;
756             this.menu.triggerEl = this.el;
757             this.addxtype(Roo.apply({}, this.menu));
758         }
759
760
761        if (this.el.hasClass('roo-button')) {
762             this.el.on('click', this.onClick, this);
763        } else {
764             this.el.select('.roo-button').on('click', this.onClick, this);
765        }
766        
767        if(this.removeClass){
768            this.el.on('click', this.onClick, this);
769        }
770        
771        this.el.enableDisplayMode();
772         
773     },
774     onClick : function(e)
775     {
776         if (this.disabled) {
777             return;
778         }
779         
780         
781         Roo.log('button on click ');
782         if(this.preventDefault){
783             e.preventDefault();
784         }
785         if (this.pressed === true || this.pressed === false) {
786             this.pressed = !this.pressed;
787             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
788             this.fireEvent('toggle', this, e, this.pressed);
789         }
790         
791         
792         this.fireEvent('click', this, e);
793     },
794     
795     /**
796      * Enables this button
797      */
798     enable : function()
799     {
800         this.disabled = false;
801         this.el.removeClass('disabled');
802     },
803     
804     /**
805      * Disable this button
806      */
807     disable : function()
808     {
809         this.disabled = true;
810         this.el.addClass('disabled');
811     },
812      /**
813      * sets the active state on/off, 
814      * @param {Boolean} state (optional) Force a particular state
815      */
816     setActive : function(v) {
817         
818         this.el[v ? 'addClass' : 'removeClass']('active');
819     },
820      /**
821      * toggles the current active state 
822      */
823     toggleActive : function()
824     {
825        var active = this.el.hasClass('active');
826        this.setActive(!active);
827        
828         
829     },
830     setText : function(str)
831     {
832         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
833     },
834     getText : function()
835     {
836         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
837     },
838     hide: function() {
839        
840      
841         this.el.hide();   
842     },
843     show: function() {
844        
845         this.el.show();   
846     }
847     
848     
849 });
850
851  /*
852  * - LGPL
853  *
854  * column
855  * 
856  */
857
858 /**
859  * @class Roo.bootstrap.Column
860  * @extends Roo.bootstrap.Component
861  * Bootstrap Column class
862  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
863  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
864  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
865  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
866  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
867  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
868  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
869  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
870  *
871  * 
872  * @cfg {Boolean} hidden (true|false) hide the element
873  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
874  * @cfg {String} fa (ban|check|...) font awesome icon
875  * @cfg {Number} fasize (1|2|....) font awsome size
876
877  * @cfg {String} icon (info-sign|check|...) glyphicon name
878
879  * @cfg {String} html content of column.
880  * 
881  * @constructor
882  * Create a new Column
883  * @param {Object} config The config object
884  */
885
886 Roo.bootstrap.Column = function(config){
887     Roo.bootstrap.Column.superclass.constructor.call(this, config);
888 };
889
890 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
891     
892     xs: false,
893     sm: false,
894     md: false,
895     lg: false,
896     xsoff: false,
897     smoff: false,
898     mdoff: false,
899     lgoff: false,
900     html: '',
901     offset: 0,
902     alert: false,
903     fa: false,
904     icon : false,
905     hidden : false,
906     fasize : 1,
907     
908     getAutoCreate : function(){
909         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
910         
911         cfg = {
912             tag: 'div',
913             cls: 'column'
914         };
915         
916         var settings=this;
917         ['xs','sm','md','lg'].map(function(size){
918             //Roo.log( size + ':' + settings[size]);
919             
920             if (settings[size+'off'] !== false) {
921                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
922             }
923             
924             if (settings[size] === false) {
925                 return;
926             }
927             
928             if (!settings[size]) { // 0 = hidden
929                 cfg.cls += ' hidden-' + size;
930                 return;
931             }
932             cfg.cls += ' col-' + size + '-' + settings[size];
933             
934         });
935         
936         if (this.hidden) {
937             cfg.cls += ' hidden';
938         }
939         
940         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
941             cfg.cls +=' alert alert-' + this.alert;
942         }
943         
944         
945         if (this.html.length) {
946             cfg.html = this.html;
947         }
948         if (this.fa) {
949             var fasize = '';
950             if (this.fasize > 1) {
951                 fasize = ' fa-' + this.fasize + 'x';
952             }
953             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
954             
955             
956         }
957         if (this.icon) {
958             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
959         }
960         
961         return cfg;
962     }
963    
964 });
965
966  
967
968  /*
969  * - LGPL
970  *
971  * page container.
972  * 
973  */
974
975
976 /**
977  * @class Roo.bootstrap.Container
978  * @extends Roo.bootstrap.Component
979  * Bootstrap Container class
980  * @cfg {Boolean} jumbotron is it a jumbotron element
981  * @cfg {String} html content of element
982  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
983  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
984  * @cfg {String} header content of header (for panel)
985  * @cfg {String} footer content of footer (for panel)
986  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
987  * @cfg {String} tag (header|aside|section) type of HTML tag.
988  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
989  * @cfg {String} fa font awesome icon
990  * @cfg {String} icon (info-sign|check|...) glyphicon name
991  * @cfg {Boolean} hidden (true|false) hide the element
992  * @cfg {Boolean} expandable (true|false) default false
993  * @cfg {Boolean} expanded (true|false) default true
994  * @cfg {String} rheader contet on the right of header
995  * @cfg {Boolean} clickable (true|false) default false
996
997  *     
998  * @constructor
999  * Create a new Container
1000  * @param {Object} config The config object
1001  */
1002
1003 Roo.bootstrap.Container = function(config){
1004     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1005     
1006     this.addEvents({
1007         // raw events
1008          /**
1009          * @event expand
1010          * After the panel has been expand
1011          * 
1012          * @param {Roo.bootstrap.Container} this
1013          */
1014         "expand" : true,
1015         /**
1016          * @event collapse
1017          * After the panel has been collapsed
1018          * 
1019          * @param {Roo.bootstrap.Container} this
1020          */
1021         "collapse" : true,
1022         /**
1023          * @event click
1024          * When a element is chick
1025          * @param {Roo.bootstrap.Container} this
1026          * @param {Roo.EventObject} e
1027          */
1028         "click" : true
1029     });
1030 };
1031
1032 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1033     
1034     jumbotron : false,
1035     well: '',
1036     panel : '',
1037     header: '',
1038     footer : '',
1039     sticky: '',
1040     tag : false,
1041     alert : false,
1042     fa: false,
1043     icon : false,
1044     expandable : false,
1045     rheader : '',
1046     expanded : true,
1047     clickable: false,
1048   
1049      
1050     getChildContainer : function() {
1051         
1052         if(!this.el){
1053             return false;
1054         }
1055         
1056         if (this.panel.length) {
1057             return this.el.select('.panel-body',true).first();
1058         }
1059         
1060         return this.el;
1061     },
1062     
1063     
1064     getAutoCreate : function(){
1065         
1066         var cfg = {
1067             tag : this.tag || 'div',
1068             html : '',
1069             cls : ''
1070         };
1071         if (this.jumbotron) {
1072             cfg.cls = 'jumbotron';
1073         }
1074         
1075         
1076         
1077         // - this is applied by the parent..
1078         //if (this.cls) {
1079         //    cfg.cls = this.cls + '';
1080         //}
1081         
1082         if (this.sticky.length) {
1083             
1084             var bd = Roo.get(document.body);
1085             if (!bd.hasClass('bootstrap-sticky')) {
1086                 bd.addClass('bootstrap-sticky');
1087                 Roo.select('html',true).setStyle('height', '100%');
1088             }
1089              
1090             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1091         }
1092         
1093         
1094         if (this.well.length) {
1095             switch (this.well) {
1096                 case 'lg':
1097                 case 'sm':
1098                     cfg.cls +=' well well-' +this.well;
1099                     break;
1100                 default:
1101                     cfg.cls +=' well';
1102                     break;
1103             }
1104         }
1105         
1106         if (this.hidden) {
1107             cfg.cls += ' hidden';
1108         }
1109         
1110         
1111         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1112             cfg.cls +=' alert alert-' + this.alert;
1113         }
1114         
1115         var body = cfg;
1116         
1117         if (this.panel.length) {
1118             cfg.cls += ' panel panel-' + this.panel;
1119             cfg.cn = [];
1120             if (this.header.length) {
1121                 
1122                 var h = [];
1123                 
1124                 if(this.expandable){
1125                     
1126                     cfg.cls = cfg.cls + ' expandable';
1127                     
1128                     h.push({
1129                         tag: 'i',
1130                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1131                     });
1132                     
1133                 }
1134                 
1135                 h.push(
1136                     {
1137                         tag: 'span',
1138                         cls : 'panel-title',
1139                         html : (this.expandable ? '&nbsp;' : '') + this.header
1140                     },
1141                     {
1142                         tag: 'span',
1143                         cls: 'panel-header-right',
1144                         html: this.rheader
1145                     }
1146                 );
1147                 
1148                 cfg.cn.push({
1149                     cls : 'panel-heading',
1150                     style : this.expandable ? 'cursor: pointer' : '',
1151                     cn : h
1152                 });
1153                 
1154             }
1155             
1156             body = false;
1157             cfg.cn.push({
1158                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1159                 html : this.html
1160             });
1161             
1162             
1163             if (this.footer.length) {
1164                 cfg.cn.push({
1165                     cls : 'panel-footer',
1166                     html : this.footer
1167                     
1168                 });
1169             }
1170             
1171         }
1172         
1173         if (body) {
1174             body.html = this.html || cfg.html;
1175             // prefix with the icons..
1176             if (this.fa) {
1177                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1178             }
1179             if (this.icon) {
1180                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1181             }
1182             
1183             
1184         }
1185         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1186             cfg.cls =  'container';
1187         }
1188         
1189         return cfg;
1190     },
1191     
1192     initEvents: function() 
1193     {
1194         if(this.expandable){
1195             var headerEl = this.headerEl();
1196         
1197             if(headerEl){
1198                 headerEl.on('click', this.onToggleClick, this);
1199             }
1200         }
1201         
1202         if(this.clickable){
1203             this.el.on('click', this.onClick, this);
1204         }
1205         
1206     },
1207     
1208     onToggleClick : function()
1209     {
1210         var headerEl = this.headerEl();
1211         
1212         if(!headerEl){
1213             return;
1214         }
1215         
1216         if(this.expanded){
1217             this.collapse();
1218             return;
1219         }
1220         
1221         this.expand();
1222     },
1223     
1224     expand : function()
1225     {
1226         if(this.fireEvent('expand', this)) {
1227             
1228             this.expanded = true;
1229             
1230             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1231             
1232             this.el.select('.panel-body',true).first().removeClass('hide');
1233             
1234             var toggleEl = this.toggleEl();
1235
1236             if(!toggleEl){
1237                 return;
1238             }
1239
1240             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1241         }
1242         
1243     },
1244     
1245     collapse : function()
1246     {
1247         if(this.fireEvent('collapse', this)) {
1248             
1249             this.expanded = false;
1250             
1251             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1252             this.el.select('.panel-body',true).first().addClass('hide');
1253         
1254             var toggleEl = this.toggleEl();
1255
1256             if(!toggleEl){
1257                 return;
1258             }
1259
1260             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1261         }
1262     },
1263     
1264     toggleEl : function()
1265     {
1266         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1267             return;
1268         }
1269         
1270         return this.el.select('.panel-heading .fa',true).first();
1271     },
1272     
1273     headerEl : function()
1274     {
1275         if(!this.el || !this.panel.length || !this.header.length){
1276             return;
1277         }
1278         
1279         return this.el.select('.panel-heading',true).first()
1280     },
1281     
1282     titleEl : function()
1283     {
1284         if(!this.el || !this.panel.length || !this.header.length){
1285             return;
1286         }
1287         
1288         return this.el.select('.panel-title',true).first();
1289     },
1290     
1291     setTitle : function(v)
1292     {
1293         var titleEl = this.titleEl();
1294         
1295         if(!titleEl){
1296             return;
1297         }
1298         
1299         titleEl.dom.innerHTML = v;
1300     },
1301     
1302     getTitle : function()
1303     {
1304         
1305         var titleEl = this.titleEl();
1306         
1307         if(!titleEl){
1308             return '';
1309         }
1310         
1311         return titleEl.dom.innerHTML;
1312     },
1313     
1314     setRightTitle : function(v)
1315     {
1316         var t = this.el.select('.panel-header-right',true).first();
1317         
1318         if(!t){
1319             return;
1320         }
1321         
1322         t.dom.innerHTML = v;
1323     },
1324     
1325     onClick : function(e)
1326     {
1327         e.preventDefault();
1328         
1329         this.fireEvent('click', this, e);
1330     }
1331    
1332 });
1333
1334  /*
1335  * - LGPL
1336  *
1337  * image
1338  * 
1339  */
1340
1341
1342 /**
1343  * @class Roo.bootstrap.Img
1344  * @extends Roo.bootstrap.Component
1345  * Bootstrap Img class
1346  * @cfg {Boolean} imgResponsive false | true
1347  * @cfg {String} border rounded | circle | thumbnail
1348  * @cfg {String} src image source
1349  * @cfg {String} alt image alternative text
1350  * @cfg {String} href a tag href
1351  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1352  * @cfg {String} xsUrl xs image source
1353  * @cfg {String} smUrl sm image source
1354  * @cfg {String} mdUrl md image source
1355  * @cfg {String} lgUrl lg image source
1356  * 
1357  * @constructor
1358  * Create a new Input
1359  * @param {Object} config The config object
1360  */
1361
1362 Roo.bootstrap.Img = function(config){
1363     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1364     
1365     this.addEvents({
1366         // img events
1367         /**
1368          * @event click
1369          * The img click event for the img.
1370          * @param {Roo.EventObject} e
1371          */
1372         "click" : true
1373     });
1374 };
1375
1376 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1377     
1378     imgResponsive: true,
1379     border: '',
1380     src: 'about:blank',
1381     href: false,
1382     target: false,
1383     xsUrl: '',
1384     smUrl: '',
1385     mdUrl: '',
1386     lgUrl: '',
1387
1388     getAutoCreate : function()
1389     {   
1390         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1391             return this.createSingleImg();
1392         }
1393         
1394         var cfg = {
1395             tag: 'div',
1396             cls: 'roo-image-responsive-group',
1397             cn: []
1398         };
1399         var _this = this;
1400         
1401         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1402             
1403             if(!_this[size + 'Url']){
1404                 return;
1405             }
1406             
1407             var img = {
1408                 tag: 'img',
1409                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1410                 html: _this.html || cfg.html,
1411                 src: _this[size + 'Url']
1412             };
1413             
1414             img.cls += ' roo-image-responsive-' + size;
1415             
1416             var s = ['xs', 'sm', 'md', 'lg'];
1417             
1418             s.splice(s.indexOf(size), 1);
1419             
1420             Roo.each(s, function(ss){
1421                 img.cls += ' hidden-' + ss;
1422             });
1423             
1424             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1425                 cfg.cls += ' img-' + _this.border;
1426             }
1427             
1428             if(_this.alt){
1429                 cfg.alt = _this.alt;
1430             }
1431             
1432             if(_this.href){
1433                 var a = {
1434                     tag: 'a',
1435                     href: _this.href,
1436                     cn: [
1437                         img
1438                     ]
1439                 };
1440
1441                 if(this.target){
1442                     a.target = _this.target;
1443                 }
1444             }
1445             
1446             cfg.cn.push((_this.href) ? a : img);
1447             
1448         });
1449         
1450         return cfg;
1451     },
1452     
1453     createSingleImg : function()
1454     {
1455         var cfg = {
1456             tag: 'img',
1457             cls: (this.imgResponsive) ? 'img-responsive' : '',
1458             html : null,
1459             src : 'about:blank'  // just incase src get's set to undefined?!?
1460         };
1461         
1462         cfg.html = this.html || cfg.html;
1463         
1464         cfg.src = this.src || cfg.src;
1465         
1466         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1467             cfg.cls += ' img-' + this.border;
1468         }
1469         
1470         if(this.alt){
1471             cfg.alt = this.alt;
1472         }
1473         
1474         if(this.href){
1475             var a = {
1476                 tag: 'a',
1477                 href: this.href,
1478                 cn: [
1479                     cfg
1480                 ]
1481             };
1482             
1483             if(this.target){
1484                 a.target = this.target;
1485             }
1486             
1487         }
1488         
1489         return (this.href) ? a : cfg;
1490     },
1491     
1492     initEvents: function() 
1493     {
1494         if(!this.href){
1495             this.el.on('click', this.onClick, this);
1496         }
1497         
1498     },
1499     
1500     onClick : function(e)
1501     {
1502         Roo.log('img onclick');
1503         this.fireEvent('click', this, e);
1504     }
1505    
1506 });
1507
1508  /*
1509  * - LGPL
1510  *
1511  * image
1512  * 
1513  */
1514
1515
1516 /**
1517  * @class Roo.bootstrap.Link
1518  * @extends Roo.bootstrap.Component
1519  * Bootstrap Link Class
1520  * @cfg {String} alt image alternative text
1521  * @cfg {String} href a tag href
1522  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1523  * @cfg {String} html the content of the link.
1524  * @cfg {String} anchor name for the anchor link
1525
1526  * @cfg {Boolean} preventDefault (true | false) default false
1527
1528  * 
1529  * @constructor
1530  * Create a new Input
1531  * @param {Object} config The config object
1532  */
1533
1534 Roo.bootstrap.Link = function(config){
1535     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1536     
1537     this.addEvents({
1538         // img events
1539         /**
1540          * @event click
1541          * The img click event for the img.
1542          * @param {Roo.EventObject} e
1543          */
1544         "click" : true
1545     });
1546 };
1547
1548 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1549     
1550     href: false,
1551     target: false,
1552     preventDefault: false,
1553     anchor : false,
1554     alt : false,
1555
1556     getAutoCreate : function()
1557     {
1558         
1559         var cfg = {
1560             tag: 'a'
1561         };
1562         // anchor's do not require html/href...
1563         if (this.anchor === false) {
1564             cfg.html = this.html || '';
1565             cfg.href = this.href || '#';
1566         } else {
1567             cfg.name = this.anchor;
1568             if (this.html !== false) {
1569                 cfg.html = this.html;
1570             }
1571             if (this.href !== false) {
1572                 cfg.href = this.href;
1573             }
1574         }
1575         
1576         if(this.alt !== false){
1577             cfg.alt = this.alt;
1578         }
1579         
1580         
1581         if(this.target !== false) {
1582             cfg.target = this.target;
1583         }
1584         
1585         return cfg;
1586     },
1587     
1588     initEvents: function() {
1589         
1590         if(!this.href || this.preventDefault){
1591             this.el.on('click', this.onClick, this);
1592         }
1593     },
1594     
1595     onClick : function(e)
1596     {
1597         if(this.preventDefault){
1598             e.preventDefault();
1599         }
1600         //Roo.log('img onclick');
1601         this.fireEvent('click', this, e);
1602     }
1603    
1604 });
1605
1606  /*
1607  * - LGPL
1608  *
1609  * header
1610  * 
1611  */
1612
1613 /**
1614  * @class Roo.bootstrap.Header
1615  * @extends Roo.bootstrap.Component
1616  * Bootstrap Header class
1617  * @cfg {String} html content of header
1618  * @cfg {Number} level (1|2|3|4|5|6) default 1
1619  * 
1620  * @constructor
1621  * Create a new Header
1622  * @param {Object} config The config object
1623  */
1624
1625
1626 Roo.bootstrap.Header  = function(config){
1627     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1628 };
1629
1630 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1631     
1632     //href : false,
1633     html : false,
1634     level : 1,
1635     
1636     
1637     
1638     getAutoCreate : function(){
1639         
1640         
1641         
1642         var cfg = {
1643             tag: 'h' + (1 *this.level),
1644             html: this.html || ''
1645         } ;
1646         
1647         return cfg;
1648     }
1649    
1650 });
1651
1652  
1653
1654  /*
1655  * Based on:
1656  * Ext JS Library 1.1.1
1657  * Copyright(c) 2006-2007, Ext JS, LLC.
1658  *
1659  * Originally Released Under LGPL - original licence link has changed is not relivant.
1660  *
1661  * Fork - LGPL
1662  * <script type="text/javascript">
1663  */
1664  
1665 /**
1666  * @class Roo.bootstrap.MenuMgr
1667  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1668  * @singleton
1669  */
1670 Roo.bootstrap.MenuMgr = function(){
1671    var menus, active, groups = {}, attached = false, lastShow = new Date();
1672
1673    // private - called when first menu is created
1674    function init(){
1675        menus = {};
1676        active = new Roo.util.MixedCollection();
1677        Roo.get(document).addKeyListener(27, function(){
1678            if(active.length > 0){
1679                hideAll();
1680            }
1681        });
1682    }
1683
1684    // private
1685    function hideAll(){
1686        if(active && active.length > 0){
1687            var c = active.clone();
1688            c.each(function(m){
1689                m.hide();
1690            });
1691        }
1692    }
1693
1694    // private
1695    function onHide(m){
1696        active.remove(m);
1697        if(active.length < 1){
1698            Roo.get(document).un("mouseup", onMouseDown);
1699             
1700            attached = false;
1701        }
1702    }
1703
1704    // private
1705    function onShow(m){
1706        var last = active.last();
1707        lastShow = new Date();
1708        active.add(m);
1709        if(!attached){
1710           Roo.get(document).on("mouseup", onMouseDown);
1711            
1712            attached = true;
1713        }
1714        if(m.parentMenu){
1715           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1716           m.parentMenu.activeChild = m;
1717        }else if(last && last.isVisible()){
1718           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1719        }
1720    }
1721
1722    // private
1723    function onBeforeHide(m){
1724        if(m.activeChild){
1725            m.activeChild.hide();
1726        }
1727        if(m.autoHideTimer){
1728            clearTimeout(m.autoHideTimer);
1729            delete m.autoHideTimer;
1730        }
1731    }
1732
1733    // private
1734    function onBeforeShow(m){
1735        var pm = m.parentMenu;
1736        if(!pm && !m.allowOtherMenus){
1737            hideAll();
1738        }else if(pm && pm.activeChild && active != m){
1739            pm.activeChild.hide();
1740        }
1741    }
1742
1743    // private this should really trigger on mouseup..
1744    function onMouseDown(e){
1745         Roo.log("on Mouse Up");
1746         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1747             Roo.log("hideAll");
1748             hideAll();
1749             e.stopEvent();
1750         }
1751         
1752         
1753    }
1754
1755    // private
1756    function onBeforeCheck(mi, state){
1757        if(state){
1758            var g = groups[mi.group];
1759            for(var i = 0, l = g.length; i < l; i++){
1760                if(g[i] != mi){
1761                    g[i].setChecked(false);
1762                }
1763            }
1764        }
1765    }
1766
1767    return {
1768
1769        /**
1770         * Hides all menus that are currently visible
1771         */
1772        hideAll : function(){
1773             hideAll();  
1774        },
1775
1776        // private
1777        register : function(menu){
1778            if(!menus){
1779                init();
1780            }
1781            menus[menu.id] = menu;
1782            menu.on("beforehide", onBeforeHide);
1783            menu.on("hide", onHide);
1784            menu.on("beforeshow", onBeforeShow);
1785            menu.on("show", onShow);
1786            var g = menu.group;
1787            if(g && menu.events["checkchange"]){
1788                if(!groups[g]){
1789                    groups[g] = [];
1790                }
1791                groups[g].push(menu);
1792                menu.on("checkchange", onCheck);
1793            }
1794        },
1795
1796         /**
1797          * Returns a {@link Roo.menu.Menu} object
1798          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1799          * be used to generate and return a new Menu instance.
1800          */
1801        get : function(menu){
1802            if(typeof menu == "string"){ // menu id
1803                return menus[menu];
1804            }else if(menu.events){  // menu instance
1805                return menu;
1806            }
1807            /*else if(typeof menu.length == 'number'){ // array of menu items?
1808                return new Roo.bootstrap.Menu({items:menu});
1809            }else{ // otherwise, must be a config
1810                return new Roo.bootstrap.Menu(menu);
1811            }
1812            */
1813            return false;
1814        },
1815
1816        // private
1817        unregister : function(menu){
1818            delete menus[menu.id];
1819            menu.un("beforehide", onBeforeHide);
1820            menu.un("hide", onHide);
1821            menu.un("beforeshow", onBeforeShow);
1822            menu.un("show", onShow);
1823            var g = menu.group;
1824            if(g && menu.events["checkchange"]){
1825                groups[g].remove(menu);
1826                menu.un("checkchange", onCheck);
1827            }
1828        },
1829
1830        // private
1831        registerCheckable : function(menuItem){
1832            var g = menuItem.group;
1833            if(g){
1834                if(!groups[g]){
1835                    groups[g] = [];
1836                }
1837                groups[g].push(menuItem);
1838                menuItem.on("beforecheckchange", onBeforeCheck);
1839            }
1840        },
1841
1842        // private
1843        unregisterCheckable : function(menuItem){
1844            var g = menuItem.group;
1845            if(g){
1846                groups[g].remove(menuItem);
1847                menuItem.un("beforecheckchange", onBeforeCheck);
1848            }
1849        }
1850    };
1851 }();/*
1852  * - LGPL
1853  *
1854  * menu
1855  * 
1856  */
1857
1858 /**
1859  * @class Roo.bootstrap.Menu
1860  * @extends Roo.bootstrap.Component
1861  * Bootstrap Menu class - container for MenuItems
1862  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1863  * 
1864  * @constructor
1865  * Create a new Menu
1866  * @param {Object} config The config object
1867  */
1868
1869
1870 Roo.bootstrap.Menu = function(config){
1871     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1872     if (this.registerMenu) {
1873         Roo.bootstrap.MenuMgr.register(this);
1874     }
1875     this.addEvents({
1876         /**
1877          * @event beforeshow
1878          * Fires before this menu is displayed
1879          * @param {Roo.menu.Menu} this
1880          */
1881         beforeshow : true,
1882         /**
1883          * @event beforehide
1884          * Fires before this menu is hidden
1885          * @param {Roo.menu.Menu} this
1886          */
1887         beforehide : true,
1888         /**
1889          * @event show
1890          * Fires after this menu is displayed
1891          * @param {Roo.menu.Menu} this
1892          */
1893         show : true,
1894         /**
1895          * @event hide
1896          * Fires after this menu is hidden
1897          * @param {Roo.menu.Menu} this
1898          */
1899         hide : true,
1900         /**
1901          * @event click
1902          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1903          * @param {Roo.menu.Menu} this
1904          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1905          * @param {Roo.EventObject} e
1906          */
1907         click : true,
1908         /**
1909          * @event mouseover
1910          * Fires when the mouse is hovering over this menu
1911          * @param {Roo.menu.Menu} this
1912          * @param {Roo.EventObject} e
1913          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1914          */
1915         mouseover : true,
1916         /**
1917          * @event mouseout
1918          * Fires when the mouse exits this menu
1919          * @param {Roo.menu.Menu} this
1920          * @param {Roo.EventObject} e
1921          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1922          */
1923         mouseout : true,
1924         /**
1925          * @event itemclick
1926          * Fires when a menu item contained in this menu is clicked
1927          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1928          * @param {Roo.EventObject} e
1929          */
1930         itemclick: true
1931     });
1932     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1933 };
1934
1935 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1936     
1937    /// html : false,
1938     //align : '',
1939     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1940     type: false,
1941     /**
1942      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1943      */
1944     registerMenu : true,
1945     
1946     menuItems :false, // stores the menu items..
1947     
1948     hidden:true,
1949     
1950     parentMenu : false,
1951     
1952     getChildContainer : function() {
1953         return this.el;  
1954     },
1955     
1956     getAutoCreate : function(){
1957          
1958         //if (['right'].indexOf(this.align)!==-1) {
1959         //    cfg.cn[1].cls += ' pull-right'
1960         //}
1961         
1962         
1963         var cfg = {
1964             tag : 'ul',
1965             cls : 'dropdown-menu' ,
1966             style : 'z-index:1000'
1967             
1968         };
1969         
1970         if (this.type === 'submenu') {
1971             cfg.cls = 'submenu active';
1972         }
1973         if (this.type === 'treeview') {
1974             cfg.cls = 'treeview-menu';
1975         }
1976         
1977         return cfg;
1978     },
1979     initEvents : function() {
1980         
1981        // Roo.log("ADD event");
1982        // Roo.log(this.triggerEl.dom);
1983         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1984         
1985         this.triggerEl.addClass('dropdown-toggle');
1986         
1987         if (Roo.isTouch) {
1988             this.el.on('touchstart'  , this.onTouch, this);
1989         }
1990         this.el.on('click' , this.onClick, this);
1991
1992         this.el.on("mouseover", this.onMouseOver, this);
1993         this.el.on("mouseout", this.onMouseOut, this);
1994         
1995     },
1996     
1997     findTargetItem : function(e)
1998     {
1999         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2000         if(!t){
2001             return false;
2002         }
2003         //Roo.log(t);         Roo.log(t.id);
2004         if(t && t.id){
2005             //Roo.log(this.menuitems);
2006             return this.menuitems.get(t.id);
2007             
2008             //return this.items.get(t.menuItemId);
2009         }
2010         
2011         return false;
2012     },
2013     
2014     onTouch : function(e) 
2015     {
2016         //e.stopEvent(); this make the user popdown broken
2017         this.onClick(e);
2018     },
2019     
2020     onClick : function(e)
2021     {
2022         Roo.log("menu.onClick");
2023         var t = this.findTargetItem(e);
2024         if(!t || t.isContainer){
2025             return;
2026         }
2027         Roo.log(e);
2028         /*
2029         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2030             if(t == this.activeItem && t.shouldDeactivate(e)){
2031                 this.activeItem.deactivate();
2032                 delete this.activeItem;
2033                 return;
2034             }
2035             if(t.canActivate){
2036                 this.setActiveItem(t, true);
2037             }
2038             return;
2039             
2040             
2041         }
2042         */
2043        
2044         Roo.log('pass click event');
2045         
2046         t.onClick(e);
2047         
2048         this.fireEvent("click", this, t, e);
2049         
2050         this.hide();
2051     },
2052      onMouseOver : function(e){
2053         var t  = this.findTargetItem(e);
2054         //Roo.log(t);
2055         //if(t){
2056         //    if(t.canActivate && !t.disabled){
2057         //        this.setActiveItem(t, true);
2058         //    }
2059         //}
2060         
2061         this.fireEvent("mouseover", this, e, t);
2062     },
2063     isVisible : function(){
2064         return !this.hidden;
2065     },
2066      onMouseOut : function(e){
2067         var t  = this.findTargetItem(e);
2068         
2069         //if(t ){
2070         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2071         //        this.activeItem.deactivate();
2072         //        delete this.activeItem;
2073         //    }
2074         //}
2075         this.fireEvent("mouseout", this, e, t);
2076     },
2077     
2078     
2079     /**
2080      * Displays this menu relative to another element
2081      * @param {String/HTMLElement/Roo.Element} element The element to align to
2082      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2083      * the element (defaults to this.defaultAlign)
2084      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2085      */
2086     show : function(el, pos, parentMenu){
2087         this.parentMenu = parentMenu;
2088         if(!this.el){
2089             this.render();
2090         }
2091         this.fireEvent("beforeshow", this);
2092         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2093     },
2094      /**
2095      * Displays this menu at a specific xy position
2096      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2097      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2098      */
2099     showAt : function(xy, parentMenu, /* private: */_e){
2100         this.parentMenu = parentMenu;
2101         if(!this.el){
2102             this.render();
2103         }
2104         if(_e !== false){
2105             this.fireEvent("beforeshow", this);
2106             //xy = this.el.adjustForConstraints(xy);
2107         }
2108         
2109         //this.el.show();
2110         this.hideMenuItems();
2111         this.hidden = false;
2112         this.triggerEl.addClass('open');
2113         
2114         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2115             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2116         }
2117         
2118         this.el.setXY(xy);
2119         this.focus();
2120         this.fireEvent("show", this);
2121     },
2122     
2123     focus : function(){
2124         return;
2125         if(!this.hidden){
2126             this.doFocus.defer(50, this);
2127         }
2128     },
2129
2130     doFocus : function(){
2131         if(!this.hidden){
2132             this.focusEl.focus();
2133         }
2134     },
2135
2136     /**
2137      * Hides this menu and optionally all parent menus
2138      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2139      */
2140     hide : function(deep){
2141         
2142         this.hideMenuItems();
2143         if(this.el && this.isVisible()){
2144             this.fireEvent("beforehide", this);
2145             if(this.activeItem){
2146                 this.activeItem.deactivate();
2147                 this.activeItem = null;
2148             }
2149             this.triggerEl.removeClass('open');;
2150             this.hidden = true;
2151             this.fireEvent("hide", this);
2152         }
2153         if(deep === true && this.parentMenu){
2154             this.parentMenu.hide(true);
2155         }
2156     },
2157     
2158     onTriggerPress  : function(e)
2159     {
2160         
2161         Roo.log('trigger press');
2162         //Roo.log(e.getTarget());
2163        // Roo.log(this.triggerEl.dom);
2164         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2165             return;
2166         }
2167         
2168         if (this.isVisible()) {
2169             Roo.log('hide');
2170             this.hide();
2171         } else {
2172             Roo.log('show');
2173             this.show(this.triggerEl, false, false);
2174         }
2175         
2176         e.stopEvent();
2177     },
2178     
2179          
2180        
2181     
2182     hideMenuItems : function()
2183     {
2184         //$(backdrop).remove()
2185         Roo.select('.open',true).each(function(aa) {
2186             
2187             aa.removeClass('open');
2188           //var parent = getParent($(this))
2189           //var relatedTarget = { relatedTarget: this }
2190           
2191            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2192           //if (e.isDefaultPrevented()) return
2193            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2194         })
2195     },
2196     addxtypeChild : function (tree, cntr) {
2197         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2198           
2199         this.menuitems.add(comp);
2200         return comp;
2201
2202     },
2203     getEl : function()
2204     {
2205         Roo.log(this.el);
2206         return this.el;
2207     }
2208 });
2209
2210  
2211  /*
2212  * - LGPL
2213  *
2214  * menu item
2215  * 
2216  */
2217
2218
2219 /**
2220  * @class Roo.bootstrap.MenuItem
2221  * @extends Roo.bootstrap.Component
2222  * Bootstrap MenuItem class
2223  * @cfg {String} html the menu label
2224  * @cfg {String} href the link
2225  * @cfg {Boolean} preventDefault (true | false) default true
2226  * @cfg {Boolean} isContainer (true | false) default false
2227  * 
2228  * 
2229  * @constructor
2230  * Create a new MenuItem
2231  * @param {Object} config The config object
2232  */
2233
2234
2235 Roo.bootstrap.MenuItem = function(config){
2236     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2237     this.addEvents({
2238         // raw events
2239         /**
2240          * @event click
2241          * The raw click event for the entire grid.
2242          * @param {Roo.bootstrap.MenuItem} this
2243          * @param {Roo.EventObject} e
2244          */
2245         "click" : true
2246     });
2247 };
2248
2249 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2250     
2251     href : false,
2252     html : false,
2253     preventDefault: true,
2254     isContainer : false,
2255     
2256     getAutoCreate : function(){
2257         
2258         if(this.isContainer){
2259             return {
2260                 tag: 'li',
2261                 cls: 'dropdown-menu-item'
2262             };
2263         }
2264         
2265         var cfg= {
2266             tag: 'li',
2267             cls: 'dropdown-menu-item',
2268             cn: [
2269                     {
2270                         tag : 'a',
2271                         href : '#',
2272                         html : 'Link'
2273                     }
2274                 ]
2275         };
2276         if (this.parent().type == 'treeview') {
2277             cfg.cls = 'treeview-menu';
2278         }
2279         
2280         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2281         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2282         return cfg;
2283     },
2284     
2285     initEvents: function() {
2286         
2287         //this.el.select('a').on('click', this.onClick, this);
2288         
2289     },
2290     onClick : function(e)
2291     {
2292         Roo.log('item on click ');
2293         //if(this.preventDefault){
2294         //    e.preventDefault();
2295         //}
2296         //this.parent().hideMenuItems();
2297         
2298         this.fireEvent('click', this, e);
2299     },
2300     getEl : function()
2301     {
2302         return this.el;
2303     }
2304 });
2305
2306  
2307
2308  /*
2309  * - LGPL
2310  *
2311  * menu separator
2312  * 
2313  */
2314
2315
2316 /**
2317  * @class Roo.bootstrap.MenuSeparator
2318  * @extends Roo.bootstrap.Component
2319  * Bootstrap MenuSeparator class
2320  * 
2321  * @constructor
2322  * Create a new MenuItem
2323  * @param {Object} config The config object
2324  */
2325
2326
2327 Roo.bootstrap.MenuSeparator = function(config){
2328     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2329 };
2330
2331 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2332     
2333     getAutoCreate : function(){
2334         var cfg = {
2335             cls: 'divider',
2336             tag : 'li'
2337         };
2338         
2339         return cfg;
2340     }
2341    
2342 });
2343
2344  
2345
2346  
2347 /*
2348 * Licence: LGPL
2349 */
2350
2351 /**
2352  * @class Roo.bootstrap.Modal
2353  * @extends Roo.bootstrap.Component
2354  * Bootstrap Modal class
2355  * @cfg {String} title Title of dialog
2356  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2357  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2358  * @cfg {Boolean} specificTitle default false
2359  * @cfg {Array} buttons Array of buttons or standard button set..
2360  * @cfg {String} buttonPosition (left|right|center) default right
2361  * @cfg {Boolean} animate default true
2362  * @cfg {Boolean} allow_close default true
2363  * 
2364  * @constructor
2365  * Create a new Modal Dialog
2366  * @param {Object} config The config object
2367  */
2368
2369 Roo.bootstrap.Modal = function(config){
2370     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2371     this.addEvents({
2372         // raw events
2373         /**
2374          * @event btnclick
2375          * The raw btnclick event for the button
2376          * @param {Roo.EventObject} e
2377          */
2378         "btnclick" : true
2379     });
2380     this.buttons = this.buttons || [];
2381      
2382     if (this.tmpl) {
2383         this.tmpl = Roo.factory(this.tmpl);
2384     }
2385     
2386 };
2387
2388 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2389     
2390     title : 'test dialog',
2391    
2392     buttons : false,
2393     
2394     // set on load...
2395      
2396     html: false,
2397     
2398     tmp: false,
2399     
2400     specificTitle: false,
2401     
2402     buttonPosition: 'right',
2403     
2404     allow_close : true,
2405     
2406     animate : true,
2407     
2408     
2409      // private
2410     bodyEl:  false,
2411     footerEl:  false,
2412     titleEl:  false,
2413     closeEl:  false,
2414     
2415     
2416     onRender : function(ct, position)
2417     {
2418         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2419      
2420         if(!this.el){
2421             var cfg = Roo.apply({},  this.getAutoCreate());
2422             cfg.id = Roo.id();
2423             //if(!cfg.name){
2424             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2425             //}
2426             //if (!cfg.name.length) {
2427             //    delete cfg.name;
2428            // }
2429             if (this.cls) {
2430                 cfg.cls += ' ' + this.cls;
2431             }
2432             if (this.style) {
2433                 cfg.style = this.style;
2434             }
2435             this.el = Roo.get(document.body).createChild(cfg, position);
2436         }
2437         //var type = this.el.dom.type;
2438         
2439         
2440         if(this.tabIndex !== undefined){
2441             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2442         }
2443         
2444         
2445         this.bodyEl = this.el.select('.modal-body',true).first();
2446         this.closeEl = this.el.select('.modal-header .close', true).first();
2447         this.footerEl = this.el.select('.modal-footer',true).first();
2448         this.titleEl = this.el.select('.modal-title',true).first();
2449         
2450         
2451          
2452         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2453         this.maskEl.enableDisplayMode("block");
2454         this.maskEl.hide();
2455         //this.el.addClass("x-dlg-modal");
2456     
2457         if (this.buttons.length) {
2458             Roo.each(this.buttons, function(bb) {
2459                 var b = Roo.apply({}, bb);
2460                 b.xns = b.xns || Roo.bootstrap;
2461                 b.xtype = b.xtype || 'Button';
2462                 if (typeof(b.listeners) == 'undefined') {
2463                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2464                 }
2465                 
2466                 var btn = Roo.factory(b);
2467                 
2468                 btn.onRender(this.el.select('.modal-footer div').first());
2469                 
2470             },this);
2471         }
2472         // render the children.
2473         var nitems = [];
2474         
2475         if(typeof(this.items) != 'undefined'){
2476             var items = this.items;
2477             delete this.items;
2478
2479             for(var i =0;i < items.length;i++) {
2480                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2481             }
2482         }
2483         
2484         this.items = nitems;
2485         
2486         // where are these used - they used to be body/close/footer
2487         
2488        
2489         this.initEvents();
2490         //this.el.addClass([this.fieldClass, this.cls]);
2491         
2492     },
2493     
2494     getAutoCreate : function(){
2495         
2496         
2497         var bdy = {
2498                 cls : 'modal-body',
2499                 html : this.html || ''
2500         };
2501         
2502         var title = {
2503             tag: 'h4',
2504             cls : 'modal-title',
2505             html : this.title
2506         };
2507         
2508         if(this.specificTitle){
2509             title = this.title;
2510             
2511         };
2512         
2513         var header = [];
2514         if (this.allow_close) {
2515             header.push({
2516                 tag: 'button',
2517                 cls : 'close',
2518                 html : '&times'
2519             });
2520         }
2521         header.push(title);
2522         
2523         var modal = {
2524             cls: "modal",
2525             style : 'display: none',
2526             cn : [
2527                 {
2528                     cls: "modal-dialog",
2529                     cn : [
2530                         {
2531                             cls : "modal-content",
2532                             cn : [
2533                                 {
2534                                     cls : 'modal-header',
2535                                     cn : header
2536                                 },
2537                                 bdy,
2538                                 {
2539                                     cls : 'modal-footer',
2540                                     cn : [
2541                                         {
2542                                             tag: 'div',
2543                                             cls: 'btn-' + this.buttonPosition
2544                                         }
2545                                     ]
2546                                     
2547                                 }
2548                                 
2549                                 
2550                             ]
2551                             
2552                         }
2553                     ]
2554                         
2555                 }
2556             ]
2557         };
2558         
2559         if(this.animate){
2560             modal.cls += ' fade';
2561         }
2562         
2563         return modal;
2564           
2565     },
2566     getChildContainer : function() {
2567          
2568          return this.bodyEl;
2569         
2570     },
2571     getButtonContainer : function() {
2572          return this.el.select('.modal-footer div',true).first();
2573         
2574     },
2575     initEvents : function()
2576     {
2577         if (this.allow_close) {
2578             this.closeEl.on('click', this.hide, this);
2579         }
2580         
2581         var _this = this;
2582         
2583         window.addEventListener("resize", function() { _this.resize(); } );
2584
2585     },
2586     
2587     resize : function()
2588     {
2589         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2590     },
2591     
2592     show : function() {
2593         
2594         if (!this.rendered) {
2595             this.render();
2596         }
2597         
2598         this.el.setStyle('display', 'block');
2599         
2600         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2601             var _this = this;
2602             (function(){
2603                 this.el.addClass('in');
2604             }).defer(50, this);
2605         }else{
2606             this.el.addClass('in');
2607             
2608         }
2609         
2610         // not sure how we can show data in here.. 
2611         //if (this.tmpl) {
2612         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2613         //}
2614         
2615         Roo.get(document.body).addClass("x-body-masked");
2616         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2617         this.maskEl.show();
2618         this.el.setStyle('zIndex', '10001');
2619        
2620         this.fireEvent('show', this);
2621          
2622         
2623         
2624     },
2625     hide : function()
2626     {
2627         this.maskEl.hide();
2628         Roo.get(document.body).removeClass("x-body-masked");
2629         this.el.removeClass('in');
2630         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2631         
2632         if(this.animate){ // why
2633             var _this = this;
2634             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2635         }else{
2636             this.el.setStyle('display', 'none');
2637         }
2638         
2639         this.fireEvent('hide', this);
2640     },
2641     
2642     addButton : function(str, cb)
2643     {
2644          
2645         
2646         var b = Roo.apply({}, { html : str } );
2647         b.xns = b.xns || Roo.bootstrap;
2648         b.xtype = b.xtype || 'Button';
2649         if (typeof(b.listeners) == 'undefined') {
2650             b.listeners = { click : cb.createDelegate(this)  };
2651         }
2652         
2653         var btn = Roo.factory(b);
2654            
2655         btn.onRender(this.el.select('.modal-footer div').first());
2656         
2657         return btn;   
2658        
2659     },
2660     
2661     setDefaultButton : function(btn)
2662     {
2663         //this.el.select('.modal-footer').()
2664     },
2665     resizeTo: function(w,h)
2666     {
2667         // skip..
2668     },
2669     setContentSize  : function(w, h)
2670     {
2671         
2672     },
2673     onButtonClick: function(btn,e)
2674     {
2675         //Roo.log([a,b,c]);
2676         this.fireEvent('btnclick', btn.name, e);
2677     },
2678      /**
2679      * Set the title of the Dialog
2680      * @param {String} str new Title
2681      */
2682     setTitle: function(str) {
2683         this.titleEl.dom.innerHTML = str;    
2684     },
2685     /**
2686      * Set the body of the Dialog
2687      * @param {String} str new Title
2688      */
2689     setBody: function(str) {
2690         this.bodyEl.dom.innerHTML = str;    
2691     },
2692     /**
2693      * Set the body of the Dialog using the template
2694      * @param {Obj} data - apply this data to the template and replace the body contents.
2695      */
2696     applyBody: function(obj)
2697     {
2698         if (!this.tmpl) {
2699             Roo.log("Error - using apply Body without a template");
2700             //code
2701         }
2702         this.tmpl.overwrite(this.bodyEl, obj);
2703     }
2704     
2705 });
2706
2707
2708 Roo.apply(Roo.bootstrap.Modal,  {
2709     /**
2710          * Button config that displays a single OK button
2711          * @type Object
2712          */
2713         OK :  [{
2714             name : 'ok',
2715             weight : 'primary',
2716             html : 'OK'
2717         }], 
2718         /**
2719          * Button config that displays Yes and No buttons
2720          * @type Object
2721          */
2722         YESNO : [
2723             {
2724                 name  : 'no',
2725                 html : 'No'
2726             },
2727             {
2728                 name  :'yes',
2729                 weight : 'primary',
2730                 html : 'Yes'
2731             }
2732         ],
2733         
2734         /**
2735          * Button config that displays OK and Cancel buttons
2736          * @type Object
2737          */
2738         OKCANCEL : [
2739             {
2740                name : 'cancel',
2741                 html : 'Cancel'
2742             },
2743             {
2744                 name : 'ok',
2745                 weight : 'primary',
2746                 html : 'OK'
2747             }
2748         ],
2749         /**
2750          * Button config that displays Yes, No and Cancel buttons
2751          * @type Object
2752          */
2753         YESNOCANCEL : [
2754             {
2755                 name : 'yes',
2756                 weight : 'primary',
2757                 html : 'Yes'
2758             },
2759             {
2760                 name : 'no',
2761                 html : 'No'
2762             },
2763             {
2764                 name : 'cancel',
2765                 html : 'Cancel'
2766             }
2767         ]
2768 });
2769  
2770  /*
2771  * - LGPL
2772  *
2773  * messagebox - can be used as a replace
2774  * 
2775  */
2776 /**
2777  * @class Roo.MessageBox
2778  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2779  * Example usage:
2780  *<pre><code>
2781 // Basic alert:
2782 Roo.Msg.alert('Status', 'Changes saved successfully.');
2783
2784 // Prompt for user data:
2785 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2786     if (btn == 'ok'){
2787         // process text value...
2788     }
2789 });
2790
2791 // Show a dialog using config options:
2792 Roo.Msg.show({
2793    title:'Save Changes?',
2794    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2795    buttons: Roo.Msg.YESNOCANCEL,
2796    fn: processResult,
2797    animEl: 'elId'
2798 });
2799 </code></pre>
2800  * @singleton
2801  */
2802 Roo.bootstrap.MessageBox = function(){
2803     var dlg, opt, mask, waitTimer;
2804     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2805     var buttons, activeTextEl, bwidth;
2806
2807     
2808     // private
2809     var handleButton = function(button){
2810         dlg.hide();
2811         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2812     };
2813
2814     // private
2815     var handleHide = function(){
2816         if(opt && opt.cls){
2817             dlg.el.removeClass(opt.cls);
2818         }
2819         //if(waitTimer){
2820         //    Roo.TaskMgr.stop(waitTimer);
2821         //    waitTimer = null;
2822         //}
2823     };
2824
2825     // private
2826     var updateButtons = function(b){
2827         var width = 0;
2828         if(!b){
2829             buttons["ok"].hide();
2830             buttons["cancel"].hide();
2831             buttons["yes"].hide();
2832             buttons["no"].hide();
2833             //dlg.footer.dom.style.display = 'none';
2834             return width;
2835         }
2836         dlg.footerEl.dom.style.display = '';
2837         for(var k in buttons){
2838             if(typeof buttons[k] != "function"){
2839                 if(b[k]){
2840                     buttons[k].show();
2841                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2842                     width += buttons[k].el.getWidth()+15;
2843                 }else{
2844                     buttons[k].hide();
2845                 }
2846             }
2847         }
2848         return width;
2849     };
2850
2851     // private
2852     var handleEsc = function(d, k, e){
2853         if(opt && opt.closable !== false){
2854             dlg.hide();
2855         }
2856         if(e){
2857             e.stopEvent();
2858         }
2859     };
2860
2861     return {
2862         /**
2863          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2864          * @return {Roo.BasicDialog} The BasicDialog element
2865          */
2866         getDialog : function(){
2867            if(!dlg){
2868                 dlg = new Roo.bootstrap.Modal( {
2869                     //draggable: true,
2870                     //resizable:false,
2871                     //constraintoviewport:false,
2872                     //fixedcenter:true,
2873                     //collapsible : false,
2874                     //shim:true,
2875                     //modal: true,
2876                   //  width:400,
2877                   //  height:100,
2878                     //buttonAlign:"center",
2879                     closeClick : function(){
2880                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2881                             handleButton("no");
2882                         }else{
2883                             handleButton("cancel");
2884                         }
2885                     }
2886                 });
2887                 dlg.render();
2888                 dlg.on("hide", handleHide);
2889                 mask = dlg.mask;
2890                 //dlg.addKeyListener(27, handleEsc);
2891                 buttons = {};
2892                 this.buttons = buttons;
2893                 var bt = this.buttonText;
2894                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2895                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2896                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2897                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2898                 //Roo.log(buttons);
2899                 bodyEl = dlg.bodyEl.createChild({
2900
2901                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2902                         '<textarea class="roo-mb-textarea"></textarea>' +
2903                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2904                 });
2905                 msgEl = bodyEl.dom.firstChild;
2906                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2907                 textboxEl.enableDisplayMode();
2908                 textboxEl.addKeyListener([10,13], function(){
2909                     if(dlg.isVisible() && opt && opt.buttons){
2910                         if(opt.buttons.ok){
2911                             handleButton("ok");
2912                         }else if(opt.buttons.yes){
2913                             handleButton("yes");
2914                         }
2915                     }
2916                 });
2917                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2918                 textareaEl.enableDisplayMode();
2919                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2920                 progressEl.enableDisplayMode();
2921                 var pf = progressEl.dom.firstChild;
2922                 if (pf) {
2923                     pp = Roo.get(pf.firstChild);
2924                     pp.setHeight(pf.offsetHeight);
2925                 }
2926                 
2927             }
2928             return dlg;
2929         },
2930
2931         /**
2932          * Updates the message box body text
2933          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2934          * the XHTML-compliant non-breaking space character '&amp;#160;')
2935          * @return {Roo.MessageBox} This message box
2936          */
2937         updateText : function(text){
2938             if(!dlg.isVisible() && !opt.width){
2939                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2940             }
2941             msgEl.innerHTML = text || '&#160;';
2942       
2943             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2944             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2945             var w = Math.max(
2946                     Math.min(opt.width || cw , this.maxWidth), 
2947                     Math.max(opt.minWidth || this.minWidth, bwidth)
2948             );
2949             if(opt.prompt){
2950                 activeTextEl.setWidth(w);
2951             }
2952             if(dlg.isVisible()){
2953                 dlg.fixedcenter = false;
2954             }
2955             // to big, make it scroll. = But as usual stupid IE does not support
2956             // !important..
2957             
2958             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2959                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2960                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2961             } else {
2962                 bodyEl.dom.style.height = '';
2963                 bodyEl.dom.style.overflowY = '';
2964             }
2965             if (cw > w) {
2966                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2967             } else {
2968                 bodyEl.dom.style.overflowX = '';
2969             }
2970             
2971             dlg.setContentSize(w, bodyEl.getHeight());
2972             if(dlg.isVisible()){
2973                 dlg.fixedcenter = true;
2974             }
2975             return this;
2976         },
2977
2978         /**
2979          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2980          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2981          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2982          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2983          * @return {Roo.MessageBox} This message box
2984          */
2985         updateProgress : function(value, text){
2986             if(text){
2987                 this.updateText(text);
2988             }
2989             if (pp) { // weird bug on my firefox - for some reason this is not defined
2990                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2991             }
2992             return this;
2993         },        
2994
2995         /**
2996          * Returns true if the message box is currently displayed
2997          * @return {Boolean} True if the message box is visible, else false
2998          */
2999         isVisible : function(){
3000             return dlg && dlg.isVisible();  
3001         },
3002
3003         /**
3004          * Hides the message box if it is displayed
3005          */
3006         hide : function(){
3007             if(this.isVisible()){
3008                 dlg.hide();
3009             }  
3010         },
3011
3012         /**
3013          * Displays a new message box, or reinitializes an existing message box, based on the config options
3014          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3015          * The following config object properties are supported:
3016          * <pre>
3017 Property    Type             Description
3018 ----------  ---------------  ------------------------------------------------------------------------------------
3019 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3020                                    closes (defaults to undefined)
3021 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3022                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3023 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3024                                    progress and wait dialogs will ignore this property and always hide the
3025                                    close button as they can only be closed programmatically.
3026 cls               String           A custom CSS class to apply to the message box element
3027 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3028                                    displayed (defaults to 75)
3029 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3030                                    function will be btn (the name of the button that was clicked, if applicable,
3031                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3032                                    Progress and wait dialogs will ignore this option since they do not respond to
3033                                    user actions and can only be closed programmatically, so any required function
3034                                    should be called by the same code after it closes the dialog.
3035 icon              String           A CSS class that provides a background image to be used as an icon for
3036                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3037 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3038 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3039 modal             Boolean          False to allow user interaction with the page while the message box is
3040                                    displayed (defaults to true)
3041 msg               String           A string that will replace the existing message box body text (defaults
3042                                    to the XHTML-compliant non-breaking space character '&#160;')
3043 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3044 progress          Boolean          True to display a progress bar (defaults to false)
3045 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3046 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3047 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3048 title             String           The title text
3049 value             String           The string value to set into the active textbox element if displayed
3050 wait              Boolean          True to display a progress bar (defaults to false)
3051 width             Number           The width of the dialog in pixels
3052 </pre>
3053          *
3054          * Example usage:
3055          * <pre><code>
3056 Roo.Msg.show({
3057    title: 'Address',
3058    msg: 'Please enter your address:',
3059    width: 300,
3060    buttons: Roo.MessageBox.OKCANCEL,
3061    multiline: true,
3062    fn: saveAddress,
3063    animEl: 'addAddressBtn'
3064 });
3065 </code></pre>
3066          * @param {Object} config Configuration options
3067          * @return {Roo.MessageBox} This message box
3068          */
3069         show : function(options)
3070         {
3071             
3072             // this causes nightmares if you show one dialog after another
3073             // especially on callbacks..
3074              
3075             if(this.isVisible()){
3076                 
3077                 this.hide();
3078                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3079                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3080                 Roo.log("New Dialog Message:" +  options.msg )
3081                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3082                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3083                 
3084             }
3085             var d = this.getDialog();
3086             opt = options;
3087             d.setTitle(opt.title || "&#160;");
3088             d.closeEl.setDisplayed(opt.closable !== false);
3089             activeTextEl = textboxEl;
3090             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3091             if(opt.prompt){
3092                 if(opt.multiline){
3093                     textboxEl.hide();
3094                     textareaEl.show();
3095                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3096                         opt.multiline : this.defaultTextHeight);
3097                     activeTextEl = textareaEl;
3098                 }else{
3099                     textboxEl.show();
3100                     textareaEl.hide();
3101                 }
3102             }else{
3103                 textboxEl.hide();
3104                 textareaEl.hide();
3105             }
3106             progressEl.setDisplayed(opt.progress === true);
3107             this.updateProgress(0);
3108             activeTextEl.dom.value = opt.value || "";
3109             if(opt.prompt){
3110                 dlg.setDefaultButton(activeTextEl);
3111             }else{
3112                 var bs = opt.buttons;
3113                 var db = null;
3114                 if(bs && bs.ok){
3115                     db = buttons["ok"];
3116                 }else if(bs && bs.yes){
3117                     db = buttons["yes"];
3118                 }
3119                 dlg.setDefaultButton(db);
3120             }
3121             bwidth = updateButtons(opt.buttons);
3122             this.updateText(opt.msg);
3123             if(opt.cls){
3124                 d.el.addClass(opt.cls);
3125             }
3126             d.proxyDrag = opt.proxyDrag === true;
3127             d.modal = opt.modal !== false;
3128             d.mask = opt.modal !== false ? mask : false;
3129             if(!d.isVisible()){
3130                 // force it to the end of the z-index stack so it gets a cursor in FF
3131                 document.body.appendChild(dlg.el.dom);
3132                 d.animateTarget = null;
3133                 d.show(options.animEl);
3134             }
3135             return this;
3136         },
3137
3138         /**
3139          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3140          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3141          * and closing the message box when the process is complete.
3142          * @param {String} title The title bar text
3143          * @param {String} msg The message box body text
3144          * @return {Roo.MessageBox} This message box
3145          */
3146         progress : function(title, msg){
3147             this.show({
3148                 title : title,
3149                 msg : msg,
3150                 buttons: false,
3151                 progress:true,
3152                 closable:false,
3153                 minWidth: this.minProgressWidth,
3154                 modal : true
3155             });
3156             return this;
3157         },
3158
3159         /**
3160          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3161          * If a callback function is passed it will be called after the user clicks the button, and the
3162          * id of the button that was clicked will be passed as the only parameter to the callback
3163          * (could also be the top-right close button).
3164          * @param {String} title The title bar text
3165          * @param {String} msg The message box body text
3166          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3167          * @param {Object} scope (optional) The scope of the callback function
3168          * @return {Roo.MessageBox} This message box
3169          */
3170         alert : function(title, msg, fn, scope){
3171             this.show({
3172                 title : title,
3173                 msg : msg,
3174                 buttons: this.OK,
3175                 fn: fn,
3176                 scope : scope,
3177                 modal : true
3178             });
3179             return this;
3180         },
3181
3182         /**
3183          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3184          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3185          * You are responsible for closing the message box when the process is complete.
3186          * @param {String} msg The message box body text
3187          * @param {String} title (optional) The title bar text
3188          * @return {Roo.MessageBox} This message box
3189          */
3190         wait : function(msg, title){
3191             this.show({
3192                 title : title,
3193                 msg : msg,
3194                 buttons: false,
3195                 closable:false,
3196                 progress:true,
3197                 modal:true,
3198                 width:300,
3199                 wait:true
3200             });
3201             waitTimer = Roo.TaskMgr.start({
3202                 run: function(i){
3203                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3204                 },
3205                 interval: 1000
3206             });
3207             return this;
3208         },
3209
3210         /**
3211          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3212          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3213          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3214          * @param {String} title The title bar text
3215          * @param {String} msg The message box body text
3216          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3217          * @param {Object} scope (optional) The scope of the callback function
3218          * @return {Roo.MessageBox} This message box
3219          */
3220         confirm : function(title, msg, fn, scope){
3221             this.show({
3222                 title : title,
3223                 msg : msg,
3224                 buttons: this.YESNO,
3225                 fn: fn,
3226                 scope : scope,
3227                 modal : true
3228             });
3229             return this;
3230         },
3231
3232         /**
3233          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3234          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3235          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3236          * (could also be the top-right close button) and the text that was entered will be passed as the two
3237          * parameters to the callback.
3238          * @param {String} title The title bar text
3239          * @param {String} msg The message box body text
3240          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3241          * @param {Object} scope (optional) The scope of the callback function
3242          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3243          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3244          * @return {Roo.MessageBox} This message box
3245          */
3246         prompt : function(title, msg, fn, scope, multiline){
3247             this.show({
3248                 title : title,
3249                 msg : msg,
3250                 buttons: this.OKCANCEL,
3251                 fn: fn,
3252                 minWidth:250,
3253                 scope : scope,
3254                 prompt:true,
3255                 multiline: multiline,
3256                 modal : true
3257             });
3258             return this;
3259         },
3260
3261         /**
3262          * Button config that displays a single OK button
3263          * @type Object
3264          */
3265         OK : {ok:true},
3266         /**
3267          * Button config that displays Yes and No buttons
3268          * @type Object
3269          */
3270         YESNO : {yes:true, no:true},
3271         /**
3272          * Button config that displays OK and Cancel buttons
3273          * @type Object
3274          */
3275         OKCANCEL : {ok:true, cancel:true},
3276         /**
3277          * Button config that displays Yes, No and Cancel buttons
3278          * @type Object
3279          */
3280         YESNOCANCEL : {yes:true, no:true, cancel:true},
3281
3282         /**
3283          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3284          * @type Number
3285          */
3286         defaultTextHeight : 75,
3287         /**
3288          * The maximum width in pixels of the message box (defaults to 600)
3289          * @type Number
3290          */
3291         maxWidth : 600,
3292         /**
3293          * The minimum width in pixels of the message box (defaults to 100)
3294          * @type Number
3295          */
3296         minWidth : 100,
3297         /**
3298          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3299          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3300          * @type Number
3301          */
3302         minProgressWidth : 250,
3303         /**
3304          * An object containing the default button text strings that can be overriden for localized language support.
3305          * Supported properties are: ok, cancel, yes and no.
3306          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3307          * @type Object
3308          */
3309         buttonText : {
3310             ok : "OK",
3311             cancel : "Cancel",
3312             yes : "Yes",
3313             no : "No"
3314         }
3315     };
3316 }();
3317
3318 /**
3319  * Shorthand for {@link Roo.MessageBox}
3320  */
3321 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3322 Roo.Msg = Roo.Msg || Roo.MessageBox;
3323 /*
3324  * - LGPL
3325  *
3326  * navbar
3327  * 
3328  */
3329
3330 /**
3331  * @class Roo.bootstrap.Navbar
3332  * @extends Roo.bootstrap.Component
3333  * Bootstrap Navbar class
3334
3335  * @constructor
3336  * Create a new Navbar
3337  * @param {Object} config The config object
3338  */
3339
3340
3341 Roo.bootstrap.Navbar = function(config){
3342     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3343     
3344 };
3345
3346 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3347     
3348     
3349    
3350     // private
3351     navItems : false,
3352     loadMask : false,
3353     
3354     
3355     getAutoCreate : function(){
3356         
3357         
3358         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3359         
3360     },
3361     
3362     initEvents :function ()
3363     {
3364         //Roo.log(this.el.select('.navbar-toggle',true));
3365         this.el.select('.navbar-toggle',true).on('click', function() {
3366            // Roo.log('click');
3367             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3368         }, this);
3369         
3370         var mark = {
3371             tag: "div",
3372             cls:"x-dlg-mask"
3373         };
3374         
3375         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3376         
3377         var size = this.el.getSize();
3378         this.maskEl.setSize(size.width, size.height);
3379         this.maskEl.enableDisplayMode("block");
3380         this.maskEl.hide();
3381         
3382         if(this.loadMask){
3383             this.maskEl.show();
3384         }
3385     },
3386     
3387     
3388     getChildContainer : function()
3389     {
3390         if (this.el.select('.collapse').getCount()) {
3391             return this.el.select('.collapse',true).first();
3392         }
3393         
3394         return this.el;
3395     },
3396     
3397     mask : function()
3398     {
3399         this.maskEl.show();
3400     },
3401     
3402     unmask : function()
3403     {
3404         this.maskEl.hide();
3405     } 
3406     
3407     
3408     
3409     
3410 });
3411
3412
3413
3414  
3415
3416  /*
3417  * - LGPL
3418  *
3419  * navbar
3420  * 
3421  */
3422
3423 /**
3424  * @class Roo.bootstrap.NavSimplebar
3425  * @extends Roo.bootstrap.Navbar
3426  * Bootstrap Sidebar class
3427  *
3428  * @cfg {Boolean} inverse is inverted color
3429  * 
3430  * @cfg {String} type (nav | pills | tabs)
3431  * @cfg {Boolean} arrangement stacked | justified
3432  * @cfg {String} align (left | right) alignment
3433  * 
3434  * @cfg {Boolean} main (true|false) main nav bar? default false
3435  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3436  * 
3437  * @cfg {String} tag (header|footer|nav|div) default is nav 
3438
3439  * 
3440  * 
3441  * 
3442  * @constructor
3443  * Create a new Sidebar
3444  * @param {Object} config The config object
3445  */
3446
3447
3448 Roo.bootstrap.NavSimplebar = function(config){
3449     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3450 };
3451
3452 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3453     
3454     inverse: false,
3455     
3456     type: false,
3457     arrangement: '',
3458     align : false,
3459     
3460     
3461     
3462     main : false,
3463     
3464     
3465     tag : false,
3466     
3467     
3468     getAutoCreate : function(){
3469         
3470         
3471         var cfg = {
3472             tag : this.tag || 'div',
3473             cls : 'navbar'
3474         };
3475           
3476         
3477         cfg.cn = [
3478             {
3479                 cls: 'nav',
3480                 tag : 'ul'
3481             }
3482         ];
3483         
3484          
3485         this.type = this.type || 'nav';
3486         if (['tabs','pills'].indexOf(this.type)!==-1) {
3487             cfg.cn[0].cls += ' nav-' + this.type
3488         
3489         
3490         } else {
3491             if (this.type!=='nav') {
3492                 Roo.log('nav type must be nav/tabs/pills')
3493             }
3494             cfg.cn[0].cls += ' navbar-nav'
3495         }
3496         
3497         
3498         
3499         
3500         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3501             cfg.cn[0].cls += ' nav-' + this.arrangement;
3502         }
3503         
3504         
3505         if (this.align === 'right') {
3506             cfg.cn[0].cls += ' navbar-right';
3507         }
3508         
3509         if (this.inverse) {
3510             cfg.cls += ' navbar-inverse';
3511             
3512         }
3513         
3514         
3515         return cfg;
3516     
3517         
3518     }
3519     
3520     
3521     
3522 });
3523
3524
3525
3526  
3527
3528  
3529        /*
3530  * - LGPL
3531  *
3532  * navbar
3533  * 
3534  */
3535
3536 /**
3537  * @class Roo.bootstrap.NavHeaderbar
3538  * @extends Roo.bootstrap.NavSimplebar
3539  * Bootstrap Sidebar class
3540  *
3541  * @cfg {String} brand what is brand
3542  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3543  * @cfg {String} brand_href href of the brand
3544  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3545  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3546  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3547  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3548  * 
3549  * @constructor
3550  * Create a new Sidebar
3551  * @param {Object} config The config object
3552  */
3553
3554
3555 Roo.bootstrap.NavHeaderbar = function(config){
3556     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3557       
3558 };
3559
3560 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3561     
3562     position: '',
3563     brand: '',
3564     brand_href: false,
3565     srButton : true,
3566     autohide : false,
3567     desktopCenter : false,
3568    
3569     
3570     getAutoCreate : function(){
3571         
3572         var   cfg = {
3573             tag: this.nav || 'nav',
3574             cls: 'navbar',
3575             role: 'navigation',
3576             cn: []
3577         };
3578         
3579         var cn = cfg.cn;
3580         if (this.desktopCenter) {
3581             cn.push({cls : 'container', cn : []});
3582             cn = cn[0].cn;
3583         }
3584         
3585         if(this.srButton){
3586             cn.push({
3587                 tag: 'div',
3588                 cls: 'navbar-header',
3589                 cn: [
3590                     {
3591                         tag: 'button',
3592                         type: 'button',
3593                         cls: 'navbar-toggle',
3594                         'data-toggle': 'collapse',
3595                         cn: [
3596                             {
3597                                 tag: 'span',
3598                                 cls: 'sr-only',
3599                                 html: 'Toggle navigation'
3600                             },
3601                             {
3602                                 tag: 'span',
3603                                 cls: 'icon-bar'
3604                             },
3605                             {
3606                                 tag: 'span',
3607                                 cls: 'icon-bar'
3608                             },
3609                             {
3610                                 tag: 'span',
3611                                 cls: 'icon-bar'
3612                             }
3613                         ]
3614                     }
3615                 ]
3616             });
3617         }
3618         
3619         cn.push({
3620             tag: 'div',
3621             cls: 'collapse navbar-collapse',
3622             cn : []
3623         });
3624         
3625         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3626         
3627         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3628             cfg.cls += ' navbar-' + this.position;
3629             
3630             // tag can override this..
3631             
3632             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3633         }
3634         
3635         if (this.brand !== '') {
3636             cn[0].cn.push({
3637                 tag: 'a',
3638                 href: this.brand_href ? this.brand_href : '#',
3639                 cls: 'navbar-brand',
3640                 cn: [
3641                 this.brand
3642                 ]
3643             });
3644         }
3645         
3646         if(this.main){
3647             cfg.cls += ' main-nav';
3648         }
3649         
3650         
3651         return cfg;
3652
3653         
3654     },
3655     getHeaderChildContainer : function()
3656     {
3657         if (this.el.select('.navbar-header').getCount()) {
3658             return this.el.select('.navbar-header',true).first();
3659         }
3660         
3661         return this.getChildContainer();
3662     },
3663     
3664     
3665     initEvents : function()
3666     {
3667         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3668         
3669         if (this.autohide) {
3670             
3671             var prevScroll = 0;
3672             var ft = this.el;
3673             
3674             Roo.get(document).on('scroll',function(e) {
3675                 var ns = Roo.get(document).getScroll().top;
3676                 var os = prevScroll;
3677                 prevScroll = ns;
3678                 
3679                 if(ns > os){
3680                     ft.removeClass('slideDown');
3681                     ft.addClass('slideUp');
3682                     return;
3683                 }
3684                 ft.removeClass('slideUp');
3685                 ft.addClass('slideDown');
3686                  
3687               
3688           },this);
3689         }
3690     }    
3691     
3692 });
3693
3694
3695
3696  
3697
3698  /*
3699  * - LGPL
3700  *
3701  * navbar
3702  * 
3703  */
3704
3705 /**
3706  * @class Roo.bootstrap.NavSidebar
3707  * @extends Roo.bootstrap.Navbar
3708  * Bootstrap Sidebar class
3709  * 
3710  * @constructor
3711  * Create a new Sidebar
3712  * @param {Object} config The config object
3713  */
3714
3715
3716 Roo.bootstrap.NavSidebar = function(config){
3717     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3718 };
3719
3720 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3721     
3722     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3723     
3724     getAutoCreate : function(){
3725         
3726         
3727         return  {
3728             tag: 'div',
3729             cls: 'sidebar sidebar-nav'
3730         };
3731     
3732         
3733     }
3734     
3735     
3736     
3737 });
3738
3739
3740
3741  
3742
3743  /*
3744  * - LGPL
3745  *
3746  * nav group
3747  * 
3748  */
3749
3750 /**
3751  * @class Roo.bootstrap.NavGroup
3752  * @extends Roo.bootstrap.Component
3753  * Bootstrap NavGroup class
3754  * @cfg {String} align (left|right)
3755  * @cfg {Boolean} inverse
3756  * @cfg {String} type (nav|pills|tab) default nav
3757  * @cfg {String} navId - reference Id for navbar.
3758
3759  * 
3760  * @constructor
3761  * Create a new nav group
3762  * @param {Object} config The config object
3763  */
3764
3765 Roo.bootstrap.NavGroup = function(config){
3766     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3767     this.navItems = [];
3768    
3769     Roo.bootstrap.NavGroup.register(this);
3770      this.addEvents({
3771         /**
3772              * @event changed
3773              * Fires when the active item changes
3774              * @param {Roo.bootstrap.NavGroup} this
3775              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3776              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3777          */
3778         'changed': true
3779      });
3780     
3781 };
3782
3783 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3784     
3785     align: '',
3786     inverse: false,
3787     form: false,
3788     type: 'nav',
3789     navId : '',
3790     // private
3791     
3792     navItems : false, 
3793     
3794     getAutoCreate : function()
3795     {
3796         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3797         
3798         cfg = {
3799             tag : 'ul',
3800             cls: 'nav' 
3801         };
3802         
3803         if (['tabs','pills'].indexOf(this.type)!==-1) {
3804             cfg.cls += ' nav-' + this.type
3805         } else {
3806             if (this.type!=='nav') {
3807                 Roo.log('nav type must be nav/tabs/pills')
3808             }
3809             cfg.cls += ' navbar-nav'
3810         }
3811         
3812         if (this.parent().sidebar) {
3813             cfg = {
3814                 tag: 'ul',
3815                 cls: 'dashboard-menu sidebar-menu'
3816             };
3817             
3818             return cfg;
3819         }
3820         
3821         if (this.form === true) {
3822             cfg = {
3823                 tag: 'form',
3824                 cls: 'navbar-form'
3825             };
3826             
3827             if (this.align === 'right') {
3828                 cfg.cls += ' navbar-right';
3829             } else {
3830                 cfg.cls += ' navbar-left';
3831             }
3832         }
3833         
3834         if (this.align === 'right') {
3835             cfg.cls += ' navbar-right';
3836         }
3837         
3838         if (this.inverse) {
3839             cfg.cls += ' navbar-inverse';
3840             
3841         }
3842         
3843         
3844         return cfg;
3845     },
3846     /**
3847     * sets the active Navigation item
3848     * @param {Roo.bootstrap.NavItem} the new current navitem
3849     */
3850     setActiveItem : function(item)
3851     {
3852         var prev = false;
3853         Roo.each(this.navItems, function(v){
3854             if (v == item) {
3855                 return ;
3856             }
3857             if (v.isActive()) {
3858                 v.setActive(false, true);
3859                 prev = v;
3860                 
3861             }
3862             
3863         });
3864
3865         item.setActive(true, true);
3866         this.fireEvent('changed', this, item, prev);
3867         
3868         
3869     },
3870     /**
3871     * gets the active Navigation item
3872     * @return {Roo.bootstrap.NavItem} the current navitem
3873     */
3874     getActive : function()
3875     {
3876         
3877         var prev = false;
3878         Roo.each(this.navItems, function(v){
3879             
3880             if (v.isActive()) {
3881                 prev = v;
3882                 
3883             }
3884             
3885         });
3886         return prev;
3887     },
3888     
3889     indexOfNav : function()
3890     {
3891         
3892         var prev = false;
3893         Roo.each(this.navItems, function(v,i){
3894             
3895             if (v.isActive()) {
3896                 prev = i;
3897                 
3898             }
3899             
3900         });
3901         return prev;
3902     },
3903     /**
3904     * adds a Navigation item
3905     * @param {Roo.bootstrap.NavItem} the navitem to add
3906     */
3907     addItem : function(cfg)
3908     {
3909         var cn = new Roo.bootstrap.NavItem(cfg);
3910         this.register(cn);
3911         cn.parentId = this.id;
3912         cn.onRender(this.el, null);
3913         return cn;
3914     },
3915     /**
3916     * register a Navigation item
3917     * @param {Roo.bootstrap.NavItem} the navitem to add
3918     */
3919     register : function(item)
3920     {
3921         this.navItems.push( item);
3922         item.navId = this.navId;
3923     
3924     },
3925     
3926     /**
3927     * clear all the Navigation item
3928     */
3929    
3930     clearAll : function()
3931     {
3932         this.navItems = [];
3933         this.el.dom.innerHTML = '';
3934     },
3935     
3936     getNavItem: function(tabId)
3937     {
3938         var ret = false;
3939         Roo.each(this.navItems, function(e) {
3940             if (e.tabId == tabId) {
3941                ret =  e;
3942                return false;
3943             }
3944             return true;
3945             
3946         });
3947         return ret;
3948     },
3949     
3950     setActiveNext : function()
3951     {
3952         var i = this.indexOfNav(this.getActive());
3953         if (i > this.navItems.length) {
3954             return;
3955         }
3956         this.setActiveItem(this.navItems[i+1]);
3957     },
3958     setActivePrev : function()
3959     {
3960         var i = this.indexOfNav(this.getActive());
3961         if (i  < 1) {
3962             return;
3963         }
3964         this.setActiveItem(this.navItems[i-1]);
3965     },
3966     clearWasActive : function(except) {
3967         Roo.each(this.navItems, function(e) {
3968             if (e.tabId != except.tabId && e.was_active) {
3969                e.was_active = false;
3970                return false;
3971             }
3972             return true;
3973             
3974         });
3975     },
3976     getWasActive : function ()
3977     {
3978         var r = false;
3979         Roo.each(this.navItems, function(e) {
3980             if (e.was_active) {
3981                r = e;
3982                return false;
3983             }
3984             return true;
3985             
3986         });
3987         return r;
3988     }
3989     
3990     
3991 });
3992
3993  
3994 Roo.apply(Roo.bootstrap.NavGroup, {
3995     
3996     groups: {},
3997      /**
3998     * register a Navigation Group
3999     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4000     */
4001     register : function(navgrp)
4002     {
4003         this.groups[navgrp.navId] = navgrp;
4004         
4005     },
4006     /**
4007     * fetch a Navigation Group based on the navigation ID
4008     * @param {string} the navgroup to add
4009     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4010     */
4011     get: function(navId) {
4012         if (typeof(this.groups[navId]) == 'undefined') {
4013             return false;
4014             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4015         }
4016         return this.groups[navId] ;
4017     }
4018     
4019     
4020     
4021 });
4022
4023  /*
4024  * - LGPL
4025  *
4026  * row
4027  * 
4028  */
4029
4030 /**
4031  * @class Roo.bootstrap.NavItem
4032  * @extends Roo.bootstrap.Component
4033  * Bootstrap Navbar.NavItem class
4034  * @cfg {String} href  link to
4035  * @cfg {String} html content of button
4036  * @cfg {String} badge text inside badge
4037  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4038  * @cfg {String} glyphicon name of glyphicon
4039  * @cfg {String} icon name of font awesome icon
4040  * @cfg {Boolean} active Is item active
4041  * @cfg {Boolean} disabled Is item disabled
4042  
4043  * @cfg {Boolean} preventDefault (true | false) default false
4044  * @cfg {String} tabId the tab that this item activates.
4045  * @cfg {String} tagtype (a|span) render as a href or span?
4046  * @cfg {Boolean} animateRef (true|false) link to element default false  
4047   
4048  * @constructor
4049  * Create a new Navbar Item
4050  * @param {Object} config The config object
4051  */
4052 Roo.bootstrap.NavItem = function(config){
4053     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4054     this.addEvents({
4055         // raw events
4056         /**
4057          * @event click
4058          * The raw click event for the entire grid.
4059          * @param {Roo.EventObject} e
4060          */
4061         "click" : true,
4062          /**
4063             * @event changed
4064             * Fires when the active item active state changes
4065             * @param {Roo.bootstrap.NavItem} this
4066             * @param {boolean} state the new state
4067              
4068          */
4069         'changed': true,
4070         /**
4071             * @event scrollto
4072             * Fires when scroll to element
4073             * @param {Roo.bootstrap.NavItem} this
4074             * @param {Object} options
4075             * @param {Roo.EventObject} e
4076              
4077          */
4078         'scrollto': true
4079     });
4080    
4081 };
4082
4083 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4084     
4085     href: false,
4086     html: '',
4087     badge: '',
4088     icon: false,
4089     glyphicon: false,
4090     active: false,
4091     preventDefault : false,
4092     tabId : false,
4093     tagtype : 'a',
4094     disabled : false,
4095     animateRef : false,
4096     was_active : false,
4097     
4098     getAutoCreate : function(){
4099          
4100         var cfg = {
4101             tag: 'li',
4102             cls: 'nav-item'
4103             
4104         };
4105         
4106         if (this.active) {
4107             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4108         }
4109         if (this.disabled) {
4110             cfg.cls += ' disabled';
4111         }
4112         
4113         if (this.href || this.html || this.glyphicon || this.icon) {
4114             cfg.cn = [
4115                 {
4116                     tag: this.tagtype,
4117                     href : this.href || "#",
4118                     html: this.html || ''
4119                 }
4120             ];
4121             
4122             if (this.icon) {
4123                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4124             }
4125
4126             if(this.glyphicon) {
4127                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4128             }
4129             
4130             if (this.menu) {
4131                 
4132                 cfg.cn[0].html += " <span class='caret'></span>";
4133              
4134             }
4135             
4136             if (this.badge !== '') {
4137                  
4138                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4139             }
4140         }
4141         
4142         
4143         
4144         return cfg;
4145     },
4146     initEvents: function() 
4147     {
4148         if (typeof (this.menu) != 'undefined') {
4149             this.menu.parentType = this.xtype;
4150             this.menu.triggerEl = this.el;
4151             this.menu = this.addxtype(Roo.apply({}, this.menu));
4152         }
4153         
4154         this.el.select('a',true).on('click', this.onClick, this);
4155         
4156         if(this.tagtype == 'span'){
4157             this.el.select('span',true).on('click', this.onClick, this);
4158         }
4159        
4160         // at this point parent should be available..
4161         this.parent().register(this);
4162     },
4163     
4164     onClick : function(e)
4165     {
4166         if(
4167                 this.preventDefault || 
4168                 this.href == '#' 
4169         ){
4170             
4171             e.preventDefault();
4172         }
4173         
4174         if (this.disabled) {
4175             return;
4176         }
4177         
4178         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4179         if (tg && tg.transition) {
4180             Roo.log("waiting for the transitionend");
4181             return;
4182         }
4183         
4184         
4185         
4186         //Roo.log("fire event clicked");
4187         if(this.fireEvent('click', this, e) === false){
4188             return;
4189         };
4190         
4191         if(this.tagtype == 'span'){
4192             return;
4193         }
4194         
4195         //Roo.log(this.href);
4196         var ael = this.el.select('a',true).first();
4197         //Roo.log(ael);
4198         
4199         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4200             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4201             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4202                 return; // ignore... - it's a 'hash' to another page.
4203             }
4204             
4205             e.preventDefault();
4206             this.scrollToElement(e);
4207         }
4208         
4209         
4210         var p =  this.parent();
4211    
4212         if (['tabs','pills'].indexOf(p.type)!==-1) {
4213             if (typeof(p.setActiveItem) !== 'undefined') {
4214                 p.setActiveItem(this);
4215             }
4216         }
4217         
4218         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4219         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4220             // remove the collapsed menu expand...
4221             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4222         }
4223     },
4224     
4225     isActive: function () {
4226         return this.active
4227     },
4228     setActive : function(state, fire, is_was_active)
4229     {
4230         if (this.active && !state && this.navId) {
4231             this.was_active = true;
4232             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4233             if (nv) {
4234                 nv.clearWasActive(this);
4235             }
4236             
4237         }
4238         this.active = state;
4239         
4240         if (!state ) {
4241             this.el.removeClass('active');
4242         } else if (!this.el.hasClass('active')) {
4243             this.el.addClass('active');
4244         }
4245         if (fire) {
4246             this.fireEvent('changed', this, state);
4247         }
4248         
4249         // show a panel if it's registered and related..
4250         
4251         if (!this.navId || !this.tabId || !state || is_was_active) {
4252             return;
4253         }
4254         
4255         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4256         if (!tg) {
4257             return;
4258         }
4259         var pan = tg.getPanelByName(this.tabId);
4260         if (!pan) {
4261             return;
4262         }
4263         // if we can not flip to new panel - go back to old nav highlight..
4264         if (false == tg.showPanel(pan)) {
4265             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4266             if (nv) {
4267                 var onav = nv.getWasActive();
4268                 if (onav) {
4269                     onav.setActive(true, false, true);
4270                 }
4271             }
4272             
4273         }
4274         
4275         
4276         
4277     },
4278      // this should not be here...
4279     setDisabled : function(state)
4280     {
4281         this.disabled = state;
4282         if (!state ) {
4283             this.el.removeClass('disabled');
4284         } else if (!this.el.hasClass('disabled')) {
4285             this.el.addClass('disabled');
4286         }
4287         
4288     },
4289     
4290     /**
4291      * Fetch the element to display the tooltip on.
4292      * @return {Roo.Element} defaults to this.el
4293      */
4294     tooltipEl : function()
4295     {
4296         return this.el.select('' + this.tagtype + '', true).first();
4297     },
4298     
4299     scrollToElement : function(e)
4300     {
4301         var c = document.body;
4302         
4303         /*
4304          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4305          */
4306         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4307             c = document.documentElement;
4308         }
4309         
4310         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4311         
4312         if(!target){
4313             return;
4314         }
4315
4316         var o = target.calcOffsetsTo(c);
4317         
4318         var options = {
4319             target : target,
4320             value : o[1]
4321         };
4322         
4323         this.fireEvent('scrollto', this, options, e);
4324         
4325         Roo.get(c).scrollTo('top', options.value, true);
4326         
4327         return;
4328     }
4329 });
4330  
4331
4332  /*
4333  * - LGPL
4334  *
4335  * sidebar item
4336  *
4337  *  li
4338  *    <span> icon </span>
4339  *    <span> text </span>
4340  *    <span>badge </span>
4341  */
4342
4343 /**
4344  * @class Roo.bootstrap.NavSidebarItem
4345  * @extends Roo.bootstrap.NavItem
4346  * Bootstrap Navbar.NavSidebarItem class
4347  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4348  * @constructor
4349  * Create a new Navbar Button
4350  * @param {Object} config The config object
4351  */
4352 Roo.bootstrap.NavSidebarItem = function(config){
4353     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4354     this.addEvents({
4355         // raw events
4356         /**
4357          * @event click
4358          * The raw click event for the entire grid.
4359          * @param {Roo.EventObject} e
4360          */
4361         "click" : true,
4362          /**
4363             * @event changed
4364             * Fires when the active item active state changes
4365             * @param {Roo.bootstrap.NavSidebarItem} this
4366             * @param {boolean} state the new state
4367              
4368          */
4369         'changed': true
4370     });
4371    
4372 };
4373
4374 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4375     
4376     badgeWeight : 'default',
4377     
4378     getAutoCreate : function(){
4379         
4380         
4381         var a = {
4382                 tag: 'a',
4383                 href : this.href || '#',
4384                 cls: '',
4385                 html : '',
4386                 cn : []
4387         };
4388         var cfg = {
4389             tag: 'li',
4390             cls: '',
4391             cn: [ a ]
4392         };
4393         var span = {
4394             tag: 'span',
4395             html : this.html || ''
4396         };
4397         
4398         
4399         if (this.active) {
4400             cfg.cls += ' active';
4401         }
4402         
4403         if (this.disabled) {
4404             cfg.cls += ' disabled';
4405         }
4406         
4407         // left icon..
4408         if (this.glyphicon || this.icon) {
4409             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4410             a.cn.push({ tag : 'i', cls : c }) ;
4411         }
4412         // html..
4413         a.cn.push(span);
4414         // then badge..
4415         if (this.badge !== '') {
4416             
4417             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4418         }
4419         // fi
4420         if (this.menu) {
4421             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4422             a.cls += 'dropdown-toggle treeview' ;
4423             
4424         }
4425         
4426         
4427         
4428         return cfg;
4429          
4430            
4431     },
4432     
4433     initEvents : function()
4434     { 
4435         this.el.on('click', this.onClick, this);
4436        
4437     
4438         if(this.badge !== ''){
4439  
4440             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4441         }
4442         
4443     },
4444     
4445     onClick : function(e)
4446     {
4447         if(this.disabled){
4448             e.preventDefault();
4449             return;
4450         }
4451         
4452         if(this.preventDefault){
4453             e.preventDefault();
4454         }
4455         
4456         this.fireEvent('click', this);
4457     },
4458     
4459     disable : function()
4460     {
4461         this.setDisabled(true);
4462     },
4463     
4464     enable : function()
4465     {
4466         this.setDisabled(false);
4467     },
4468     
4469     setDisabled : function(state)
4470     {
4471         if(this.disabled == state){
4472             return;
4473         }
4474         
4475         this.disabled = state;
4476         
4477         if (state) {
4478             this.el.addClass('disabled');
4479             return;
4480         }
4481         
4482         this.el.removeClass('disabled');
4483         
4484         return;
4485     },
4486     
4487     setActive : function(state)
4488     {
4489         if(this.active == state){
4490             return;
4491         }
4492         
4493         this.active = state;
4494         
4495         if (state) {
4496             this.el.addClass('active');
4497             return;
4498         }
4499         
4500         this.el.removeClass('active');
4501         
4502         return;
4503     },
4504     
4505     isActive: function () 
4506     {
4507         return this.active;
4508     },
4509     
4510     setBadge : function(str)
4511     {
4512         if(!this.badgeEl){
4513             return;
4514         }
4515         
4516         this.badgeEl.dom.innerHTML = str;
4517     }
4518     
4519    
4520      
4521  
4522 });
4523  
4524
4525  /*
4526  * - LGPL
4527  *
4528  * row
4529  * 
4530  */
4531
4532 /**
4533  * @class Roo.bootstrap.Row
4534  * @extends Roo.bootstrap.Component
4535  * Bootstrap Row class (contains columns...)
4536  * 
4537  * @constructor
4538  * Create a new Row
4539  * @param {Object} config The config object
4540  */
4541
4542 Roo.bootstrap.Row = function(config){
4543     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4544 };
4545
4546 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4547     
4548     getAutoCreate : function(){
4549        return {
4550             cls: 'row clearfix'
4551        };
4552     }
4553     
4554     
4555 });
4556
4557  
4558
4559  /*
4560  * - LGPL
4561  *
4562  * element
4563  * 
4564  */
4565
4566 /**
4567  * @class Roo.bootstrap.Element
4568  * @extends Roo.bootstrap.Component
4569  * Bootstrap Element class
4570  * @cfg {String} html contents of the element
4571  * @cfg {String} tag tag of the element
4572  * @cfg {String} cls class of the element
4573  * @cfg {Boolean} preventDefault (true|false) default false
4574  * @cfg {Boolean} clickable (true|false) default false
4575  * 
4576  * @constructor
4577  * Create a new Element
4578  * @param {Object} config The config object
4579  */
4580
4581 Roo.bootstrap.Element = function(config){
4582     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4583     
4584     this.addEvents({
4585         // raw events
4586         /**
4587          * @event click
4588          * When a element is chick
4589          * @param {Roo.bootstrap.Element} this
4590          * @param {Roo.EventObject} e
4591          */
4592         "click" : true
4593     });
4594 };
4595
4596 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4597     
4598     tag: 'div',
4599     cls: '',
4600     html: '',
4601     preventDefault: false, 
4602     clickable: false,
4603     
4604     getAutoCreate : function(){
4605         
4606         var cfg = {
4607             tag: this.tag,
4608             cls: this.cls,
4609             html: this.html
4610         };
4611         
4612         return cfg;
4613     },
4614     
4615     initEvents: function() 
4616     {
4617         Roo.bootstrap.Element.superclass.initEvents.call(this);
4618         
4619         if(this.clickable){
4620             this.el.on('click', this.onClick, this);
4621         }
4622         
4623     },
4624     
4625     onClick : function(e)
4626     {
4627         if(this.preventDefault){
4628             e.preventDefault();
4629         }
4630         
4631         this.fireEvent('click', this, e);
4632     },
4633     
4634     getValue : function()
4635     {
4636         return this.el.dom.innerHTML;
4637     },
4638     
4639     setValue : function(value)
4640     {
4641         this.el.dom.innerHTML = value;
4642     }
4643    
4644 });
4645
4646  
4647
4648  /*
4649  * - LGPL
4650  *
4651  * pagination
4652  * 
4653  */
4654
4655 /**
4656  * @class Roo.bootstrap.Pagination
4657  * @extends Roo.bootstrap.Component
4658  * Bootstrap Pagination class
4659  * @cfg {String} size xs | sm | md | lg
4660  * @cfg {Boolean} inverse false | true
4661  * 
4662  * @constructor
4663  * Create a new Pagination
4664  * @param {Object} config The config object
4665  */
4666
4667 Roo.bootstrap.Pagination = function(config){
4668     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4669 };
4670
4671 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4672     
4673     cls: false,
4674     size: false,
4675     inverse: false,
4676     
4677     getAutoCreate : function(){
4678         var cfg = {
4679             tag: 'ul',
4680                 cls: 'pagination'
4681         };
4682         if (this.inverse) {
4683             cfg.cls += ' inverse';
4684         }
4685         if (this.html) {
4686             cfg.html=this.html;
4687         }
4688         if (this.cls) {
4689             cfg.cls += " " + this.cls;
4690         }
4691         return cfg;
4692     }
4693    
4694 });
4695
4696  
4697
4698  /*
4699  * - LGPL
4700  *
4701  * Pagination item
4702  * 
4703  */
4704
4705
4706 /**
4707  * @class Roo.bootstrap.PaginationItem
4708  * @extends Roo.bootstrap.Component
4709  * Bootstrap PaginationItem class
4710  * @cfg {String} html text
4711  * @cfg {String} href the link
4712  * @cfg {Boolean} preventDefault (true | false) default true
4713  * @cfg {Boolean} active (true | false) default false
4714  * @cfg {Boolean} disabled default false
4715  * 
4716  * 
4717  * @constructor
4718  * Create a new PaginationItem
4719  * @param {Object} config The config object
4720  */
4721
4722
4723 Roo.bootstrap.PaginationItem = function(config){
4724     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4725     this.addEvents({
4726         // raw events
4727         /**
4728          * @event click
4729          * The raw click event for the entire grid.
4730          * @param {Roo.EventObject} e
4731          */
4732         "click" : true
4733     });
4734 };
4735
4736 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4737     
4738     href : false,
4739     html : false,
4740     preventDefault: true,
4741     active : false,
4742     cls : false,
4743     disabled: false,
4744     
4745     getAutoCreate : function(){
4746         var cfg= {
4747             tag: 'li',
4748             cn: [
4749                 {
4750                     tag : 'a',
4751                     href : this.href ? this.href : '#',
4752                     html : this.html ? this.html : ''
4753                 }
4754             ]
4755         };
4756         
4757         if(this.cls){
4758             cfg.cls = this.cls;
4759         }
4760         
4761         if(this.disabled){
4762             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4763         }
4764         
4765         if(this.active){
4766             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4767         }
4768         
4769         return cfg;
4770     },
4771     
4772     initEvents: function() {
4773         
4774         this.el.on('click', this.onClick, this);
4775         
4776     },
4777     onClick : function(e)
4778     {
4779         Roo.log('PaginationItem on click ');
4780         if(this.preventDefault){
4781             e.preventDefault();
4782         }
4783         
4784         if(this.disabled){
4785             return;
4786         }
4787         
4788         this.fireEvent('click', this, e);
4789     }
4790    
4791 });
4792
4793  
4794
4795  /*
4796  * - LGPL
4797  *
4798  * slider
4799  * 
4800  */
4801
4802
4803 /**
4804  * @class Roo.bootstrap.Slider
4805  * @extends Roo.bootstrap.Component
4806  * Bootstrap Slider class
4807  *    
4808  * @constructor
4809  * Create a new Slider
4810  * @param {Object} config The config object
4811  */
4812
4813 Roo.bootstrap.Slider = function(config){
4814     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4815 };
4816
4817 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4818     
4819     getAutoCreate : function(){
4820         
4821         var cfg = {
4822             tag: 'div',
4823             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4824             cn: [
4825                 {
4826                     tag: 'a',
4827                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4828                 }
4829             ]
4830         };
4831         
4832         return cfg;
4833     }
4834    
4835 });
4836
4837  /*
4838  * Based on:
4839  * Ext JS Library 1.1.1
4840  * Copyright(c) 2006-2007, Ext JS, LLC.
4841  *
4842  * Originally Released Under LGPL - original licence link has changed is not relivant.
4843  *
4844  * Fork - LGPL
4845  * <script type="text/javascript">
4846  */
4847  
4848
4849 /**
4850  * @class Roo.grid.ColumnModel
4851  * @extends Roo.util.Observable
4852  * This is the default implementation of a ColumnModel used by the Grid. It defines
4853  * the columns in the grid.
4854  * <br>Usage:<br>
4855  <pre><code>
4856  var colModel = new Roo.grid.ColumnModel([
4857         {header: "Ticker", width: 60, sortable: true, locked: true},
4858         {header: "Company Name", width: 150, sortable: true},
4859         {header: "Market Cap.", width: 100, sortable: true},
4860         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4861         {header: "Employees", width: 100, sortable: true, resizable: false}
4862  ]);
4863  </code></pre>
4864  * <p>
4865  
4866  * The config options listed for this class are options which may appear in each
4867  * individual column definition.
4868  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4869  * @constructor
4870  * @param {Object} config An Array of column config objects. See this class's
4871  * config objects for details.
4872 */
4873 Roo.grid.ColumnModel = function(config){
4874         /**
4875      * The config passed into the constructor
4876      */
4877     this.config = config;
4878     this.lookup = {};
4879
4880     // if no id, create one
4881     // if the column does not have a dataIndex mapping,
4882     // map it to the order it is in the config
4883     for(var i = 0, len = config.length; i < len; i++){
4884         var c = config[i];
4885         if(typeof c.dataIndex == "undefined"){
4886             c.dataIndex = i;
4887         }
4888         if(typeof c.renderer == "string"){
4889             c.renderer = Roo.util.Format[c.renderer];
4890         }
4891         if(typeof c.id == "undefined"){
4892             c.id = Roo.id();
4893         }
4894         if(c.editor && c.editor.xtype){
4895             c.editor  = Roo.factory(c.editor, Roo.grid);
4896         }
4897         if(c.editor && c.editor.isFormField){
4898             c.editor = new Roo.grid.GridEditor(c.editor);
4899         }
4900         this.lookup[c.id] = c;
4901     }
4902
4903     /**
4904      * The width of columns which have no width specified (defaults to 100)
4905      * @type Number
4906      */
4907     this.defaultWidth = 100;
4908
4909     /**
4910      * Default sortable of columns which have no sortable specified (defaults to false)
4911      * @type Boolean
4912      */
4913     this.defaultSortable = false;
4914
4915     this.addEvents({
4916         /**
4917              * @event widthchange
4918              * Fires when the width of a column changes.
4919              * @param {ColumnModel} this
4920              * @param {Number} columnIndex The column index
4921              * @param {Number} newWidth The new width
4922              */
4923             "widthchange": true,
4924         /**
4925              * @event headerchange
4926              * Fires when the text of a header changes.
4927              * @param {ColumnModel} this
4928              * @param {Number} columnIndex The column index
4929              * @param {Number} newText The new header text
4930              */
4931             "headerchange": true,
4932         /**
4933              * @event hiddenchange
4934              * Fires when a column is hidden or "unhidden".
4935              * @param {ColumnModel} this
4936              * @param {Number} columnIndex The column index
4937              * @param {Boolean} hidden true if hidden, false otherwise
4938              */
4939             "hiddenchange": true,
4940             /**
4941          * @event columnmoved
4942          * Fires when a column is moved.
4943          * @param {ColumnModel} this
4944          * @param {Number} oldIndex
4945          * @param {Number} newIndex
4946          */
4947         "columnmoved" : true,
4948         /**
4949          * @event columlockchange
4950          * Fires when a column's locked state is changed
4951          * @param {ColumnModel} this
4952          * @param {Number} colIndex
4953          * @param {Boolean} locked true if locked
4954          */
4955         "columnlockchange" : true
4956     });
4957     Roo.grid.ColumnModel.superclass.constructor.call(this);
4958 };
4959 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4960     /**
4961      * @cfg {String} header The header text to display in the Grid view.
4962      */
4963     /**
4964      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4965      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4966      * specified, the column's index is used as an index into the Record's data Array.
4967      */
4968     /**
4969      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4970      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4971      */
4972     /**
4973      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4974      * Defaults to the value of the {@link #defaultSortable} property.
4975      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4976      */
4977     /**
4978      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4979      */
4980     /**
4981      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4982      */
4983     /**
4984      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4985      */
4986     /**
4987      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4988      */
4989     /**
4990      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4991      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4992      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4993      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4994      */
4995        /**
4996      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4997      */
4998     /**
4999      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5000      */
5001     /**
5002      * @cfg {String} cursor (Optional)
5003      */
5004     /**
5005      * @cfg {String} tooltip (Optional)
5006      */
5007     /**
5008      * @cfg {Number} xs (Optional)
5009      */
5010     /**
5011      * @cfg {Number} sm (Optional)
5012      */
5013     /**
5014      * @cfg {Number} md (Optional)
5015      */
5016     /**
5017      * @cfg {Number} lg (Optional)
5018      */
5019     /**
5020      * Returns the id of the column at the specified index.
5021      * @param {Number} index The column index
5022      * @return {String} the id
5023      */
5024     getColumnId : function(index){
5025         return this.config[index].id;
5026     },
5027
5028     /**
5029      * Returns the column for a specified id.
5030      * @param {String} id The column id
5031      * @return {Object} the column
5032      */
5033     getColumnById : function(id){
5034         return this.lookup[id];
5035     },
5036
5037     
5038     /**
5039      * Returns the column for a specified dataIndex.
5040      * @param {String} dataIndex The column dataIndex
5041      * @return {Object|Boolean} the column or false if not found
5042      */
5043     getColumnByDataIndex: function(dataIndex){
5044         var index = this.findColumnIndex(dataIndex);
5045         return index > -1 ? this.config[index] : false;
5046     },
5047     
5048     /**
5049      * Returns the index for a specified column id.
5050      * @param {String} id The column id
5051      * @return {Number} the index, or -1 if not found
5052      */
5053     getIndexById : function(id){
5054         for(var i = 0, len = this.config.length; i < len; i++){
5055             if(this.config[i].id == id){
5056                 return i;
5057             }
5058         }
5059         return -1;
5060     },
5061     
5062     /**
5063      * Returns the index for a specified column dataIndex.
5064      * @param {String} dataIndex The column dataIndex
5065      * @return {Number} the index, or -1 if not found
5066      */
5067     
5068     findColumnIndex : function(dataIndex){
5069         for(var i = 0, len = this.config.length; i < len; i++){
5070             if(this.config[i].dataIndex == dataIndex){
5071                 return i;
5072             }
5073         }
5074         return -1;
5075     },
5076     
5077     
5078     moveColumn : function(oldIndex, newIndex){
5079         var c = this.config[oldIndex];
5080         this.config.splice(oldIndex, 1);
5081         this.config.splice(newIndex, 0, c);
5082         this.dataMap = null;
5083         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5084     },
5085
5086     isLocked : function(colIndex){
5087         return this.config[colIndex].locked === true;
5088     },
5089
5090     setLocked : function(colIndex, value, suppressEvent){
5091         if(this.isLocked(colIndex) == value){
5092             return;
5093         }
5094         this.config[colIndex].locked = value;
5095         if(!suppressEvent){
5096             this.fireEvent("columnlockchange", this, colIndex, value);
5097         }
5098     },
5099
5100     getTotalLockedWidth : function(){
5101         var totalWidth = 0;
5102         for(var i = 0; i < this.config.length; i++){
5103             if(this.isLocked(i) && !this.isHidden(i)){
5104                 this.totalWidth += this.getColumnWidth(i);
5105             }
5106         }
5107         return totalWidth;
5108     },
5109
5110     getLockedCount : function(){
5111         for(var i = 0, len = this.config.length; i < len; i++){
5112             if(!this.isLocked(i)){
5113                 return i;
5114             }
5115         }
5116     },
5117
5118     /**
5119      * Returns the number of columns.
5120      * @return {Number}
5121      */
5122     getColumnCount : function(visibleOnly){
5123         if(visibleOnly === true){
5124             var c = 0;
5125             for(var i = 0, len = this.config.length; i < len; i++){
5126                 if(!this.isHidden(i)){
5127                     c++;
5128                 }
5129             }
5130             return c;
5131         }
5132         return this.config.length;
5133     },
5134
5135     /**
5136      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5137      * @param {Function} fn
5138      * @param {Object} scope (optional)
5139      * @return {Array} result
5140      */
5141     getColumnsBy : function(fn, scope){
5142         var r = [];
5143         for(var i = 0, len = this.config.length; i < len; i++){
5144             var c = this.config[i];
5145             if(fn.call(scope||this, c, i) === true){
5146                 r[r.length] = c;
5147             }
5148         }
5149         return r;
5150     },
5151
5152     /**
5153      * Returns true if the specified column is sortable.
5154      * @param {Number} col The column index
5155      * @return {Boolean}
5156      */
5157     isSortable : function(col){
5158         if(typeof this.config[col].sortable == "undefined"){
5159             return this.defaultSortable;
5160         }
5161         return this.config[col].sortable;
5162     },
5163
5164     /**
5165      * Returns the rendering (formatting) function defined for the column.
5166      * @param {Number} col The column index.
5167      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5168      */
5169     getRenderer : function(col){
5170         if(!this.config[col].renderer){
5171             return Roo.grid.ColumnModel.defaultRenderer;
5172         }
5173         return this.config[col].renderer;
5174     },
5175
5176     /**
5177      * Sets the rendering (formatting) function for a column.
5178      * @param {Number} col The column index
5179      * @param {Function} fn The function to use to process the cell's raw data
5180      * to return HTML markup for the grid view. The render function is called with
5181      * the following parameters:<ul>
5182      * <li>Data value.</li>
5183      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5184      * <li>css A CSS style string to apply to the table cell.</li>
5185      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5186      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5187      * <li>Row index</li>
5188      * <li>Column index</li>
5189      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5190      */
5191     setRenderer : function(col, fn){
5192         this.config[col].renderer = fn;
5193     },
5194
5195     /**
5196      * Returns the width for the specified column.
5197      * @param {Number} col The column index
5198      * @return {Number}
5199      */
5200     getColumnWidth : function(col){
5201         return this.config[col].width * 1 || this.defaultWidth;
5202     },
5203
5204     /**
5205      * Sets the width for a column.
5206      * @param {Number} col The column index
5207      * @param {Number} width The new width
5208      */
5209     setColumnWidth : function(col, width, suppressEvent){
5210         this.config[col].width = width;
5211         this.totalWidth = null;
5212         if(!suppressEvent){
5213              this.fireEvent("widthchange", this, col, width);
5214         }
5215     },
5216
5217     /**
5218      * Returns the total width of all columns.
5219      * @param {Boolean} includeHidden True to include hidden column widths
5220      * @return {Number}
5221      */
5222     getTotalWidth : function(includeHidden){
5223         if(!this.totalWidth){
5224             this.totalWidth = 0;
5225             for(var i = 0, len = this.config.length; i < len; i++){
5226                 if(includeHidden || !this.isHidden(i)){
5227                     this.totalWidth += this.getColumnWidth(i);
5228                 }
5229             }
5230         }
5231         return this.totalWidth;
5232     },
5233
5234     /**
5235      * Returns the header for the specified column.
5236      * @param {Number} col The column index
5237      * @return {String}
5238      */
5239     getColumnHeader : function(col){
5240         return this.config[col].header;
5241     },
5242
5243     /**
5244      * Sets the header for a column.
5245      * @param {Number} col The column index
5246      * @param {String} header The new header
5247      */
5248     setColumnHeader : function(col, header){
5249         this.config[col].header = header;
5250         this.fireEvent("headerchange", this, col, header);
5251     },
5252
5253     /**
5254      * Returns the tooltip for the specified column.
5255      * @param {Number} col The column index
5256      * @return {String}
5257      */
5258     getColumnTooltip : function(col){
5259             return this.config[col].tooltip;
5260     },
5261     /**
5262      * Sets the tooltip for a column.
5263      * @param {Number} col The column index
5264      * @param {String} tooltip The new tooltip
5265      */
5266     setColumnTooltip : function(col, tooltip){
5267             this.config[col].tooltip = tooltip;
5268     },
5269
5270     /**
5271      * Returns the dataIndex for the specified column.
5272      * @param {Number} col The column index
5273      * @return {Number}
5274      */
5275     getDataIndex : function(col){
5276         return this.config[col].dataIndex;
5277     },
5278
5279     /**
5280      * Sets the dataIndex for a column.
5281      * @param {Number} col The column index
5282      * @param {Number} dataIndex The new dataIndex
5283      */
5284     setDataIndex : function(col, dataIndex){
5285         this.config[col].dataIndex = dataIndex;
5286     },
5287
5288     
5289     
5290     /**
5291      * Returns true if the cell is editable.
5292      * @param {Number} colIndex The column index
5293      * @param {Number} rowIndex The row index - this is nto actually used..?
5294      * @return {Boolean}
5295      */
5296     isCellEditable : function(colIndex, rowIndex){
5297         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5298     },
5299
5300     /**
5301      * Returns the editor defined for the cell/column.
5302      * return false or null to disable editing.
5303      * @param {Number} colIndex The column index
5304      * @param {Number} rowIndex The row index
5305      * @return {Object}
5306      */
5307     getCellEditor : function(colIndex, rowIndex){
5308         return this.config[colIndex].editor;
5309     },
5310
5311     /**
5312      * Sets if a column is editable.
5313      * @param {Number} col The column index
5314      * @param {Boolean} editable True if the column is editable
5315      */
5316     setEditable : function(col, editable){
5317         this.config[col].editable = editable;
5318     },
5319
5320
5321     /**
5322      * Returns true if the column is hidden.
5323      * @param {Number} colIndex The column index
5324      * @return {Boolean}
5325      */
5326     isHidden : function(colIndex){
5327         return this.config[colIndex].hidden;
5328     },
5329
5330
5331     /**
5332      * Returns true if the column width cannot be changed
5333      */
5334     isFixed : function(colIndex){
5335         return this.config[colIndex].fixed;
5336     },
5337
5338     /**
5339      * Returns true if the column can be resized
5340      * @return {Boolean}
5341      */
5342     isResizable : function(colIndex){
5343         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5344     },
5345     /**
5346      * Sets if a column is hidden.
5347      * @param {Number} colIndex The column index
5348      * @param {Boolean} hidden True if the column is hidden
5349      */
5350     setHidden : function(colIndex, hidden){
5351         this.config[colIndex].hidden = hidden;
5352         this.totalWidth = null;
5353         this.fireEvent("hiddenchange", this, colIndex, hidden);
5354     },
5355
5356     /**
5357      * Sets the editor for a column.
5358      * @param {Number} col The column index
5359      * @param {Object} editor The editor object
5360      */
5361     setEditor : function(col, editor){
5362         this.config[col].editor = editor;
5363     }
5364 });
5365
5366 Roo.grid.ColumnModel.defaultRenderer = function(value){
5367         if(typeof value == "string" && value.length < 1){
5368             return "&#160;";
5369         }
5370         return value;
5371 };
5372
5373 // Alias for backwards compatibility
5374 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5375 /*
5376  * Based on:
5377  * Ext JS Library 1.1.1
5378  * Copyright(c) 2006-2007, Ext JS, LLC.
5379  *
5380  * Originally Released Under LGPL - original licence link has changed is not relivant.
5381  *
5382  * Fork - LGPL
5383  * <script type="text/javascript">
5384  */
5385  
5386 /**
5387  * @class Roo.LoadMask
5388  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5389  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5390  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5391  * element's UpdateManager load indicator and will be destroyed after the initial load.
5392  * @constructor
5393  * Create a new LoadMask
5394  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5395  * @param {Object} config The config object
5396  */
5397 Roo.LoadMask = function(el, config){
5398     this.el = Roo.get(el);
5399     Roo.apply(this, config);
5400     if(this.store){
5401         this.store.on('beforeload', this.onBeforeLoad, this);
5402         this.store.on('load', this.onLoad, this);
5403         this.store.on('loadexception', this.onLoadException, this);
5404         this.removeMask = false;
5405     }else{
5406         var um = this.el.getUpdateManager();
5407         um.showLoadIndicator = false; // disable the default indicator
5408         um.on('beforeupdate', this.onBeforeLoad, this);
5409         um.on('update', this.onLoad, this);
5410         um.on('failure', this.onLoad, this);
5411         this.removeMask = true;
5412     }
5413 };
5414
5415 Roo.LoadMask.prototype = {
5416     /**
5417      * @cfg {Boolean} removeMask
5418      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5419      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5420      */
5421     /**
5422      * @cfg {String} msg
5423      * The text to display in a centered loading message box (defaults to 'Loading...')
5424      */
5425     msg : 'Loading...',
5426     /**
5427      * @cfg {String} msgCls
5428      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5429      */
5430     msgCls : 'x-mask-loading',
5431
5432     /**
5433      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5434      * @type Boolean
5435      */
5436     disabled: false,
5437
5438     /**
5439      * Disables the mask to prevent it from being displayed
5440      */
5441     disable : function(){
5442        this.disabled = true;
5443     },
5444
5445     /**
5446      * Enables the mask so that it can be displayed
5447      */
5448     enable : function(){
5449         this.disabled = false;
5450     },
5451     
5452     onLoadException : function()
5453     {
5454         Roo.log(arguments);
5455         
5456         if (typeof(arguments[3]) != 'undefined') {
5457             Roo.MessageBox.alert("Error loading",arguments[3]);
5458         } 
5459         /*
5460         try {
5461             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5462                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5463             }   
5464         } catch(e) {
5465             
5466         }
5467         */
5468     
5469         
5470         
5471         this.el.unmask(this.removeMask);
5472     },
5473     // private
5474     onLoad : function()
5475     {
5476         this.el.unmask(this.removeMask);
5477     },
5478
5479     // private
5480     onBeforeLoad : function(){
5481         if(!this.disabled){
5482             this.el.mask(this.msg, this.msgCls);
5483         }
5484     },
5485
5486     // private
5487     destroy : function(){
5488         if(this.store){
5489             this.store.un('beforeload', this.onBeforeLoad, this);
5490             this.store.un('load', this.onLoad, this);
5491             this.store.un('loadexception', this.onLoadException, this);
5492         }else{
5493             var um = this.el.getUpdateManager();
5494             um.un('beforeupdate', this.onBeforeLoad, this);
5495             um.un('update', this.onLoad, this);
5496             um.un('failure', this.onLoad, this);
5497         }
5498     }
5499 };/*
5500  * - LGPL
5501  *
5502  * table
5503  * 
5504  */
5505
5506 /**
5507  * @class Roo.bootstrap.Table
5508  * @extends Roo.bootstrap.Component
5509  * Bootstrap Table class
5510  * @cfg {String} cls table class
5511  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5512  * @cfg {String} bgcolor Specifies the background color for a table
5513  * @cfg {Number} border Specifies whether the table cells should have borders or not
5514  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5515  * @cfg {Number} cellspacing Specifies the space between cells
5516  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5517  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5518  * @cfg {String} sortable Specifies that the table should be sortable
5519  * @cfg {String} summary Specifies a summary of the content of a table
5520  * @cfg {Number} width Specifies the width of a table
5521  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5522  * 
5523  * @cfg {boolean} striped Should the rows be alternative striped
5524  * @cfg {boolean} bordered Add borders to the table
5525  * @cfg {boolean} hover Add hover highlighting
5526  * @cfg {boolean} condensed Format condensed
5527  * @cfg {boolean} responsive Format condensed
5528  * @cfg {Boolean} loadMask (true|false) default false
5529  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5530  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5531  * @cfg {Boolean} rowSelection (true|false) default false
5532  * @cfg {Boolean} cellSelection (true|false) default false
5533  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5534  
5535  * 
5536  * @constructor
5537  * Create a new Table
5538  * @param {Object} config The config object
5539  */
5540
5541 Roo.bootstrap.Table = function(config){
5542     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5543     
5544     // BC...
5545     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5546     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5547     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5548     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5549     
5550     
5551     if (this.sm) {
5552         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5553         this.sm = this.selModel;
5554         this.sm.xmodule = this.xmodule || false;
5555     }
5556     if (this.cm && typeof(this.cm.config) == 'undefined') {
5557         this.colModel = new Roo.grid.ColumnModel(this.cm);
5558         this.cm = this.colModel;
5559         this.cm.xmodule = this.xmodule || false;
5560     }
5561     if (this.store) {
5562         this.store= Roo.factory(this.store, Roo.data);
5563         this.ds = this.store;
5564         this.ds.xmodule = this.xmodule || false;
5565          
5566     }
5567     if (this.footer && this.store) {
5568         this.footer.dataSource = this.ds;
5569         this.footer = Roo.factory(this.footer);
5570     }
5571     
5572     /** @private */
5573     this.addEvents({
5574         /**
5575          * @event cellclick
5576          * Fires when a cell is clicked
5577          * @param {Roo.bootstrap.Table} this
5578          * @param {Roo.Element} el
5579          * @param {Number} rowIndex
5580          * @param {Number} columnIndex
5581          * @param {Roo.EventObject} e
5582          */
5583         "cellclick" : true,
5584         /**
5585          * @event celldblclick
5586          * Fires when a cell is double clicked
5587          * @param {Roo.bootstrap.Table} this
5588          * @param {Roo.Element} el
5589          * @param {Number} rowIndex
5590          * @param {Number} columnIndex
5591          * @param {Roo.EventObject} e
5592          */
5593         "celldblclick" : true,
5594         /**
5595          * @event rowclick
5596          * Fires when a row is clicked
5597          * @param {Roo.bootstrap.Table} this
5598          * @param {Roo.Element} el
5599          * @param {Number} rowIndex
5600          * @param {Roo.EventObject} e
5601          */
5602         "rowclick" : true,
5603         /**
5604          * @event rowdblclick
5605          * Fires when a row is double clicked
5606          * @param {Roo.bootstrap.Table} this
5607          * @param {Roo.Element} el
5608          * @param {Number} rowIndex
5609          * @param {Roo.EventObject} e
5610          */
5611         "rowdblclick" : true,
5612         /**
5613          * @event mouseover
5614          * Fires when a mouseover occur
5615          * @param {Roo.bootstrap.Table} this
5616          * @param {Roo.Element} el
5617          * @param {Number} rowIndex
5618          * @param {Number} columnIndex
5619          * @param {Roo.EventObject} e
5620          */
5621         "mouseover" : true,
5622         /**
5623          * @event mouseout
5624          * Fires when a mouseout occur
5625          * @param {Roo.bootstrap.Table} this
5626          * @param {Roo.Element} el
5627          * @param {Number} rowIndex
5628          * @param {Number} columnIndex
5629          * @param {Roo.EventObject} e
5630          */
5631         "mouseout" : true,
5632         /**
5633          * @event rowclass
5634          * Fires when a row is rendered, so you can change add a style to it.
5635          * @param {Roo.bootstrap.Table} this
5636          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5637          */
5638         'rowclass' : true,
5639           /**
5640          * @event rowsrendered
5641          * Fires when all the  rows have been rendered
5642          * @param {Roo.bootstrap.Table} this
5643          */
5644         'rowsrendered' : true
5645         
5646     });
5647 };
5648
5649 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5650     
5651     cls: false,
5652     align: false,
5653     bgcolor: false,
5654     border: false,
5655     cellpadding: false,
5656     cellspacing: false,
5657     frame: false,
5658     rules: false,
5659     sortable: false,
5660     summary: false,
5661     width: false,
5662     striped : false,
5663     bordered: false,
5664     hover:  false,
5665     condensed : false,
5666     responsive : false,
5667     sm : false,
5668     cm : false,
5669     store : false,
5670     loadMask : false,
5671     footerShow : true,
5672     headerShow : true,
5673   
5674     rowSelection : false,
5675     cellSelection : false,
5676     layout : false,
5677     
5678     // Roo.Element - the tbody
5679     mainBody: false, 
5680     
5681     getAutoCreate : function(){
5682         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5683         
5684         cfg = {
5685             tag: 'table',
5686             cls : 'table',
5687             cn : []
5688         };
5689             
5690         if (this.striped) {
5691             cfg.cls += ' table-striped';
5692         }
5693         
5694         if (this.hover) {
5695             cfg.cls += ' table-hover';
5696         }
5697         if (this.bordered) {
5698             cfg.cls += ' table-bordered';
5699         }
5700         if (this.condensed) {
5701             cfg.cls += ' table-condensed';
5702         }
5703         if (this.responsive) {
5704             cfg.cls += ' table-responsive';
5705         }
5706         
5707         if (this.cls) {
5708             cfg.cls+=  ' ' +this.cls;
5709         }
5710         
5711         // this lot should be simplifed...
5712         
5713         if (this.align) {
5714             cfg.align=this.align;
5715         }
5716         if (this.bgcolor) {
5717             cfg.bgcolor=this.bgcolor;
5718         }
5719         if (this.border) {
5720             cfg.border=this.border;
5721         }
5722         if (this.cellpadding) {
5723             cfg.cellpadding=this.cellpadding;
5724         }
5725         if (this.cellspacing) {
5726             cfg.cellspacing=this.cellspacing;
5727         }
5728         if (this.frame) {
5729             cfg.frame=this.frame;
5730         }
5731         if (this.rules) {
5732             cfg.rules=this.rules;
5733         }
5734         if (this.sortable) {
5735             cfg.sortable=this.sortable;
5736         }
5737         if (this.summary) {
5738             cfg.summary=this.summary;
5739         }
5740         if (this.width) {
5741             cfg.width=this.width;
5742         }
5743         if (this.layout) {
5744             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5745         }
5746         
5747         if(this.store || this.cm){
5748             if(this.headerShow){
5749                 cfg.cn.push(this.renderHeader());
5750             }
5751             
5752             cfg.cn.push(this.renderBody());
5753             
5754             if(this.footerShow){
5755                 cfg.cn.push(this.renderFooter());
5756             }
5757             
5758             cfg.cls+=  ' TableGrid';
5759         }
5760         
5761         return { cn : [ cfg ] };
5762     },
5763     
5764     initEvents : function()
5765     {   
5766         if(!this.store || !this.cm){
5767             return;
5768         }
5769         
5770         //Roo.log('initEvents with ds!!!!');
5771         
5772         this.mainBody = this.el.select('tbody', true).first();
5773         
5774         
5775         var _this = this;
5776         
5777         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5778             e.on('click', _this.sort, _this);
5779         });
5780         
5781         this.el.on("click", this.onClick, this);
5782         this.el.on("dblclick", this.onDblClick, this);
5783         
5784         // why is this done????? = it breaks dialogs??
5785         //this.parent().el.setStyle('position', 'relative');
5786         
5787         
5788         if (this.footer) {
5789             this.footer.parentId = this.id;
5790             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5791         }
5792         
5793         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5794         
5795         this.store.on('load', this.onLoad, this);
5796         this.store.on('beforeload', this.onBeforeLoad, this);
5797         this.store.on('update', this.onUpdate, this);
5798         this.store.on('add', this.onAdd, this);
5799         
5800     },
5801     
5802     onMouseover : function(e, el)
5803     {
5804         var cell = Roo.get(el);
5805         
5806         if(!cell){
5807             return;
5808         }
5809         
5810         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5811             cell = cell.findParent('td', false, true);
5812         }
5813         
5814         var row = cell.findParent('tr', false, true);
5815         var cellIndex = cell.dom.cellIndex;
5816         var rowIndex = row.dom.rowIndex - 1; // start from 0
5817         
5818         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5819         
5820     },
5821     
5822     onMouseout : function(e, el)
5823     {
5824         var cell = Roo.get(el);
5825         
5826         if(!cell){
5827             return;
5828         }
5829         
5830         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5831             cell = cell.findParent('td', false, true);
5832         }
5833         
5834         var row = cell.findParent('tr', false, true);
5835         var cellIndex = cell.dom.cellIndex;
5836         var rowIndex = row.dom.rowIndex - 1; // start from 0
5837         
5838         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5839         
5840     },
5841     
5842     onClick : function(e, el)
5843     {
5844         var cell = Roo.get(el);
5845         
5846         if(!cell || (!this.cellSelection && !this.rowSelection)){
5847             return;
5848         }
5849         
5850         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5851             cell = cell.findParent('td', false, true);
5852         }
5853         
5854         if(!cell || typeof(cell) == 'undefined'){
5855             return;
5856         }
5857         
5858         var row = cell.findParent('tr', false, true);
5859         
5860         if(!row || typeof(row) == 'undefined'){
5861             return;
5862         }
5863         
5864         var cellIndex = cell.dom.cellIndex;
5865         var rowIndex = this.getRowIndex(row);
5866         
5867         // why??? - should these not be based on SelectionModel?
5868         if(this.cellSelection){
5869             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5870         }
5871         
5872         if(this.rowSelection){
5873             this.fireEvent('rowclick', this, row, rowIndex, e);
5874         }
5875         
5876         
5877     },
5878     
5879     onDblClick : function(e,el)
5880     {
5881         var cell = Roo.get(el);
5882         
5883         if(!cell || (!this.CellSelection && !this.RowSelection)){
5884             return;
5885         }
5886         
5887         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5888             cell = cell.findParent('td', false, true);
5889         }
5890         
5891         if(!cell || typeof(cell) == 'undefined'){
5892             return;
5893         }
5894         
5895         var row = cell.findParent('tr', false, true);
5896         
5897         if(!row || typeof(row) == 'undefined'){
5898             return;
5899         }
5900         
5901         var cellIndex = cell.dom.cellIndex;
5902         var rowIndex = this.getRowIndex(row);
5903         
5904         if(this.CellSelection){
5905             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5906         }
5907         
5908         if(this.RowSelection){
5909             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5910         }
5911     },
5912     
5913     sort : function(e,el)
5914     {
5915         var col = Roo.get(el);
5916         
5917         if(!col.hasClass('sortable')){
5918             return;
5919         }
5920         
5921         var sort = col.attr('sort');
5922         var dir = 'ASC';
5923         
5924         if(col.hasClass('glyphicon-arrow-up')){
5925             dir = 'DESC';
5926         }
5927         
5928         this.store.sortInfo = {field : sort, direction : dir};
5929         
5930         if (this.footer) {
5931             Roo.log("calling footer first");
5932             this.footer.onClick('first');
5933         } else {
5934         
5935             this.store.load({ params : { start : 0 } });
5936         }
5937     },
5938     
5939     renderHeader : function()
5940     {
5941         var header = {
5942             tag: 'thead',
5943             cn : []
5944         };
5945         
5946         var cm = this.cm;
5947         
5948         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5949             
5950             var config = cm.config[i];
5951             
5952             var c = {
5953                 tag: 'th',
5954                 style : '',
5955                 html: cm.getColumnHeader(i)
5956             };
5957             
5958             var hh = '';
5959             
5960             if(typeof(config.lgHeader) != 'undefined'){
5961                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5962             }
5963             
5964             if(typeof(config.mdHeader) != 'undefined'){
5965                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5966             }
5967             
5968             if(typeof(config.smHeader) != 'undefined'){
5969                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5970             }
5971             
5972             if(typeof(config.xsHeader) != 'undefined'){
5973                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5974             }
5975             
5976             if(hh.length){
5977                 c.html = hh;
5978             }
5979             
5980             if(typeof(config.tooltip) != 'undefined'){
5981                 c.tooltip = config.tooltip;
5982             }
5983             
5984             if(typeof(config.colspan) != 'undefined'){
5985                 c.colspan = config.colspan;
5986             }
5987             
5988             if(typeof(config.hidden) != 'undefined' && config.hidden){
5989                 c.style += ' display:none;';
5990             }
5991             
5992             if(typeof(config.dataIndex) != 'undefined'){
5993                 c.sort = config.dataIndex;
5994             }
5995             
5996             if(typeof(config.sortable) != 'undefined' && config.sortable){
5997                 c.cls = 'sortable';
5998             }
5999             
6000             if(typeof(config.align) != 'undefined' && config.align.length){
6001                 c.style += ' text-align:' + config.align + ';';
6002             }
6003             
6004             if(typeof(config.width) != 'undefined'){
6005                 c.style += ' width:' + config.width + 'px;';
6006             }
6007             
6008             if(typeof(config.cls) != 'undefined'){
6009                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6010             }
6011             
6012             ['xs','sm','md','lg'].map(function(size){
6013                 
6014                 if(typeof(config[size]) == 'undefined'){
6015                     return;
6016                 }
6017                 
6018                 if (!config[size]) { // 0 = hidden
6019                     c.cls += ' hidden-' + size;
6020                     return;
6021                 }
6022                 
6023                 c.cls += ' col-' + size + '-' + config[size];
6024
6025             });
6026             
6027             header.cn.push(c)
6028         }
6029         
6030         return header;
6031     },
6032     
6033     renderBody : function()
6034     {
6035         var body = {
6036             tag: 'tbody',
6037             cn : [
6038                 {
6039                     tag: 'tr',
6040                     cn : [
6041                         {
6042                             tag : 'td',
6043                             colspan :  this.cm.getColumnCount()
6044                         }
6045                     ]
6046                 }
6047             ]
6048         };
6049         
6050         return body;
6051     },
6052     
6053     renderFooter : function()
6054     {
6055         var footer = {
6056             tag: 'tfoot',
6057             cn : [
6058                 {
6059                     tag: 'tr',
6060                     cn : [
6061                         {
6062                             tag : 'td',
6063                             colspan :  this.cm.getColumnCount()
6064                         }
6065                     ]
6066                 }
6067             ]
6068         };
6069         
6070         return footer;
6071     },
6072     
6073     
6074     
6075     onLoad : function()
6076     {
6077 //        Roo.log('ds onload');
6078         this.clear();
6079         
6080         var _this = this;
6081         var cm = this.cm;
6082         var ds = this.store;
6083         
6084         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6085             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6086             
6087             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6088                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6089             }
6090             
6091             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6092                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6093             }
6094         });
6095         
6096         var tbody =  this.mainBody;
6097               
6098         if(ds.getCount() > 0){
6099             ds.data.each(function(d,rowIndex){
6100                 var row =  this.renderRow(cm, ds, rowIndex);
6101                 
6102                 tbody.createChild(row);
6103                 
6104                 var _this = this;
6105                 
6106                 if(row.cellObjects.length){
6107                     Roo.each(row.cellObjects, function(r){
6108                         _this.renderCellObject(r);
6109                     })
6110                 }
6111                 
6112             }, this);
6113         }
6114         
6115         Roo.each(this.el.select('tbody td', true).elements, function(e){
6116             e.on('mouseover', _this.onMouseover, _this);
6117         });
6118         
6119         Roo.each(this.el.select('tbody td', true).elements, function(e){
6120             e.on('mouseout', _this.onMouseout, _this);
6121         });
6122         this.fireEvent('rowsrendered', this);
6123         //if(this.loadMask){
6124         //    this.maskEl.hide();
6125         //}
6126     },
6127     
6128     
6129     onUpdate : function(ds,record)
6130     {
6131         this.refreshRow(record);
6132     },
6133     
6134     onRemove : function(ds, record, index, isUpdate){
6135         if(isUpdate !== true){
6136             this.fireEvent("beforerowremoved", this, index, record);
6137         }
6138         var bt = this.mainBody.dom;
6139         
6140         var rows = this.el.select('tbody > tr', true).elements;
6141         
6142         if(typeof(rows[index]) != 'undefined'){
6143             bt.removeChild(rows[index].dom);
6144         }
6145         
6146 //        if(bt.rows[index]){
6147 //            bt.removeChild(bt.rows[index]);
6148 //        }
6149         
6150         if(isUpdate !== true){
6151             //this.stripeRows(index);
6152             //this.syncRowHeights(index, index);
6153             //this.layout();
6154             this.fireEvent("rowremoved", this, index, record);
6155         }
6156     },
6157     
6158     onAdd : function(ds, records, rowIndex)
6159     {
6160         //Roo.log('on Add called');
6161         // - note this does not handle multiple adding very well..
6162         var bt = this.mainBody.dom;
6163         for (var i =0 ; i < records.length;i++) {
6164             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6165             //Roo.log(records[i]);
6166             //Roo.log(this.store.getAt(rowIndex+i));
6167             this.insertRow(this.store, rowIndex + i, false);
6168             return;
6169         }
6170         
6171     },
6172     
6173     
6174     refreshRow : function(record){
6175         var ds = this.store, index;
6176         if(typeof record == 'number'){
6177             index = record;
6178             record = ds.getAt(index);
6179         }else{
6180             index = ds.indexOf(record);
6181         }
6182         this.insertRow(ds, index, true);
6183         this.onRemove(ds, record, index+1, true);
6184         //this.syncRowHeights(index, index);
6185         //this.layout();
6186         this.fireEvent("rowupdated", this, index, record);
6187     },
6188     
6189     insertRow : function(dm, rowIndex, isUpdate){
6190         
6191         if(!isUpdate){
6192             this.fireEvent("beforerowsinserted", this, rowIndex);
6193         }
6194             //var s = this.getScrollState();
6195         var row = this.renderRow(this.cm, this.store, rowIndex);
6196         // insert before rowIndex..
6197         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6198         
6199         var _this = this;
6200                 
6201         if(row.cellObjects.length){
6202             Roo.each(row.cellObjects, function(r){
6203                 _this.renderCellObject(r);
6204             })
6205         }
6206             
6207         if(!isUpdate){
6208             this.fireEvent("rowsinserted", this, rowIndex);
6209             //this.syncRowHeights(firstRow, lastRow);
6210             //this.stripeRows(firstRow);
6211             //this.layout();
6212         }
6213         
6214     },
6215     
6216     
6217     getRowDom : function(rowIndex)
6218     {
6219         var rows = this.el.select('tbody > tr', true).elements;
6220         
6221         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6222         
6223     },
6224     // returns the object tree for a tr..
6225   
6226     
6227     renderRow : function(cm, ds, rowIndex) 
6228     {
6229         
6230         var d = ds.getAt(rowIndex);
6231         
6232         var row = {
6233             tag : 'tr',
6234             cn : []
6235         };
6236             
6237         var cellObjects = [];
6238         
6239         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6240             var config = cm.config[i];
6241             
6242             var renderer = cm.getRenderer(i);
6243             var value = '';
6244             var id = false;
6245             
6246             if(typeof(renderer) !== 'undefined'){
6247                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6248             }
6249             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6250             // and are rendered into the cells after the row is rendered - using the id for the element.
6251             
6252             if(typeof(value) === 'object'){
6253                 id = Roo.id();
6254                 cellObjects.push({
6255                     container : id,
6256                     cfg : value 
6257                 })
6258             }
6259             
6260             var rowcfg = {
6261                 record: d,
6262                 rowIndex : rowIndex,
6263                 colIndex : i,
6264                 rowClass : ''
6265             };
6266
6267             this.fireEvent('rowclass', this, rowcfg);
6268             
6269             var td = {
6270                 tag: 'td',
6271                 cls : rowcfg.rowClass,
6272                 style: '',
6273                 html: (typeof(value) === 'object') ? '' : value
6274             };
6275             
6276             if (id) {
6277                 td.id = id;
6278             }
6279             
6280             if(typeof(config.colspan) != 'undefined'){
6281                 td.colspan = config.colspan;
6282             }
6283             
6284             if(typeof(config.hidden) != 'undefined' && config.hidden){
6285                 td.style += ' display:none;';
6286             }
6287             
6288             if(typeof(config.align) != 'undefined' && config.align.length){
6289                 td.style += ' text-align:' + config.align + ';';
6290             }
6291             
6292             if(typeof(config.width) != 'undefined'){
6293                 td.style += ' width:' +  config.width + 'px;';
6294             }
6295             
6296             if(typeof(config.cursor) != 'undefined'){
6297                 td.style += ' cursor:' +  config.cursor + ';';
6298             }
6299             
6300             if(typeof(config.cls) != 'undefined'){
6301                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6302             }
6303             
6304             ['xs','sm','md','lg'].map(function(size){
6305                 
6306                 if(typeof(config[size]) == 'undefined'){
6307                     return;
6308                 }
6309                 
6310                 if (!config[size]) { // 0 = hidden
6311                     td.cls += ' hidden-' + size;
6312                     return;
6313                 }
6314                 
6315                 td.cls += ' col-' + size + '-' + config[size];
6316
6317             });
6318              
6319             row.cn.push(td);
6320            
6321         }
6322         
6323         row.cellObjects = cellObjects;
6324         
6325         return row;
6326           
6327     },
6328     
6329     
6330     
6331     onBeforeLoad : function()
6332     {
6333         //Roo.log('ds onBeforeLoad');
6334         
6335         //this.clear();
6336         
6337         //if(this.loadMask){
6338         //    this.maskEl.show();
6339         //}
6340     },
6341      /**
6342      * Remove all rows
6343      */
6344     clear : function()
6345     {
6346         this.el.select('tbody', true).first().dom.innerHTML = '';
6347     },
6348     /**
6349      * Show or hide a row.
6350      * @param {Number} rowIndex to show or hide
6351      * @param {Boolean} state hide
6352      */
6353     setRowVisibility : function(rowIndex, state)
6354     {
6355         var bt = this.mainBody.dom;
6356         
6357         var rows = this.el.select('tbody > tr', true).elements;
6358         
6359         if(typeof(rows[rowIndex]) == 'undefined'){
6360             return;
6361         }
6362         rows[rowIndex].dom.style.display = state ? '' : 'none';
6363     },
6364     
6365     
6366     getSelectionModel : function(){
6367         if(!this.selModel){
6368             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6369         }
6370         return this.selModel;
6371     },
6372     /*
6373      * Render the Roo.bootstrap object from renderder
6374      */
6375     renderCellObject : function(r)
6376     {
6377         var _this = this;
6378         
6379         var t = r.cfg.render(r.container);
6380         
6381         if(r.cfg.cn){
6382             Roo.each(r.cfg.cn, function(c){
6383                 var child = {
6384                     container: t.getChildContainer(),
6385                     cfg: c
6386                 };
6387                 _this.renderCellObject(child);
6388             })
6389         }
6390     },
6391     
6392     getRowIndex : function(row)
6393     {
6394         var rowIndex = -1;
6395         
6396         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6397             if(el != row){
6398                 return;
6399             }
6400             
6401             rowIndex = index;
6402         });
6403         
6404         return rowIndex;
6405     }
6406    
6407 });
6408
6409  
6410
6411  /*
6412  * - LGPL
6413  *
6414  * table cell
6415  * 
6416  */
6417
6418 /**
6419  * @class Roo.bootstrap.TableCell
6420  * @extends Roo.bootstrap.Component
6421  * Bootstrap TableCell class
6422  * @cfg {String} html cell contain text
6423  * @cfg {String} cls cell class
6424  * @cfg {String} tag cell tag (td|th) default td
6425  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6426  * @cfg {String} align Aligns the content in a cell
6427  * @cfg {String} axis Categorizes cells
6428  * @cfg {String} bgcolor Specifies the background color of a cell
6429  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6430  * @cfg {Number} colspan Specifies the number of columns a cell should span
6431  * @cfg {String} headers Specifies one or more header cells a cell is related to
6432  * @cfg {Number} height Sets the height of a cell
6433  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6434  * @cfg {Number} rowspan Sets the number of rows a cell should span
6435  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6436  * @cfg {String} valign Vertical aligns the content in a cell
6437  * @cfg {Number} width Specifies the width of a cell
6438  * 
6439  * @constructor
6440  * Create a new TableCell
6441  * @param {Object} config The config object
6442  */
6443
6444 Roo.bootstrap.TableCell = function(config){
6445     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6446 };
6447
6448 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6449     
6450     html: false,
6451     cls: false,
6452     tag: false,
6453     abbr: false,
6454     align: false,
6455     axis: false,
6456     bgcolor: false,
6457     charoff: false,
6458     colspan: false,
6459     headers: false,
6460     height: false,
6461     nowrap: false,
6462     rowspan: false,
6463     scope: false,
6464     valign: false,
6465     width: false,
6466     
6467     
6468     getAutoCreate : function(){
6469         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6470         
6471         cfg = {
6472             tag: 'td'
6473         };
6474         
6475         if(this.tag){
6476             cfg.tag = this.tag;
6477         }
6478         
6479         if (this.html) {
6480             cfg.html=this.html
6481         }
6482         if (this.cls) {
6483             cfg.cls=this.cls
6484         }
6485         if (this.abbr) {
6486             cfg.abbr=this.abbr
6487         }
6488         if (this.align) {
6489             cfg.align=this.align
6490         }
6491         if (this.axis) {
6492             cfg.axis=this.axis
6493         }
6494         if (this.bgcolor) {
6495             cfg.bgcolor=this.bgcolor
6496         }
6497         if (this.charoff) {
6498             cfg.charoff=this.charoff
6499         }
6500         if (this.colspan) {
6501             cfg.colspan=this.colspan
6502         }
6503         if (this.headers) {
6504             cfg.headers=this.headers
6505         }
6506         if (this.height) {
6507             cfg.height=this.height
6508         }
6509         if (this.nowrap) {
6510             cfg.nowrap=this.nowrap
6511         }
6512         if (this.rowspan) {
6513             cfg.rowspan=this.rowspan
6514         }
6515         if (this.scope) {
6516             cfg.scope=this.scope
6517         }
6518         if (this.valign) {
6519             cfg.valign=this.valign
6520         }
6521         if (this.width) {
6522             cfg.width=this.width
6523         }
6524         
6525         
6526         return cfg;
6527     }
6528    
6529 });
6530
6531  
6532
6533  /*
6534  * - LGPL
6535  *
6536  * table row
6537  * 
6538  */
6539
6540 /**
6541  * @class Roo.bootstrap.TableRow
6542  * @extends Roo.bootstrap.Component
6543  * Bootstrap TableRow class
6544  * @cfg {String} cls row class
6545  * @cfg {String} align Aligns the content in a table row
6546  * @cfg {String} bgcolor Specifies a background color for a table row
6547  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6548  * @cfg {String} valign Vertical aligns the content in a table row
6549  * 
6550  * @constructor
6551  * Create a new TableRow
6552  * @param {Object} config The config object
6553  */
6554
6555 Roo.bootstrap.TableRow = function(config){
6556     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6557 };
6558
6559 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6560     
6561     cls: false,
6562     align: false,
6563     bgcolor: false,
6564     charoff: false,
6565     valign: false,
6566     
6567     getAutoCreate : function(){
6568         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6569         
6570         cfg = {
6571             tag: 'tr'
6572         };
6573             
6574         if(this.cls){
6575             cfg.cls = this.cls;
6576         }
6577         if(this.align){
6578             cfg.align = this.align;
6579         }
6580         if(this.bgcolor){
6581             cfg.bgcolor = this.bgcolor;
6582         }
6583         if(this.charoff){
6584             cfg.charoff = this.charoff;
6585         }
6586         if(this.valign){
6587             cfg.valign = this.valign;
6588         }
6589         
6590         return cfg;
6591     }
6592    
6593 });
6594
6595  
6596
6597  /*
6598  * - LGPL
6599  *
6600  * table body
6601  * 
6602  */
6603
6604 /**
6605  * @class Roo.bootstrap.TableBody
6606  * @extends Roo.bootstrap.Component
6607  * Bootstrap TableBody class
6608  * @cfg {String} cls element class
6609  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6610  * @cfg {String} align Aligns the content inside the element
6611  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6612  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6613  * 
6614  * @constructor
6615  * Create a new TableBody
6616  * @param {Object} config The config object
6617  */
6618
6619 Roo.bootstrap.TableBody = function(config){
6620     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6621 };
6622
6623 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6624     
6625     cls: false,
6626     tag: false,
6627     align: false,
6628     charoff: false,
6629     valign: false,
6630     
6631     getAutoCreate : function(){
6632         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6633         
6634         cfg = {
6635             tag: 'tbody'
6636         };
6637             
6638         if (this.cls) {
6639             cfg.cls=this.cls
6640         }
6641         if(this.tag){
6642             cfg.tag = this.tag;
6643         }
6644         
6645         if(this.align){
6646             cfg.align = this.align;
6647         }
6648         if(this.charoff){
6649             cfg.charoff = this.charoff;
6650         }
6651         if(this.valign){
6652             cfg.valign = this.valign;
6653         }
6654         
6655         return cfg;
6656     }
6657     
6658     
6659 //    initEvents : function()
6660 //    {
6661 //        
6662 //        if(!this.store){
6663 //            return;
6664 //        }
6665 //        
6666 //        this.store = Roo.factory(this.store, Roo.data);
6667 //        this.store.on('load', this.onLoad, this);
6668 //        
6669 //        this.store.load();
6670 //        
6671 //    },
6672 //    
6673 //    onLoad: function () 
6674 //    {   
6675 //        this.fireEvent('load', this);
6676 //    }
6677 //    
6678 //   
6679 });
6680
6681  
6682
6683  /*
6684  * Based on:
6685  * Ext JS Library 1.1.1
6686  * Copyright(c) 2006-2007, Ext JS, LLC.
6687  *
6688  * Originally Released Under LGPL - original licence link has changed is not relivant.
6689  *
6690  * Fork - LGPL
6691  * <script type="text/javascript">
6692  */
6693
6694 // as we use this in bootstrap.
6695 Roo.namespace('Roo.form');
6696  /**
6697  * @class Roo.form.Action
6698  * Internal Class used to handle form actions
6699  * @constructor
6700  * @param {Roo.form.BasicForm} el The form element or its id
6701  * @param {Object} config Configuration options
6702  */
6703
6704  
6705  
6706 // define the action interface
6707 Roo.form.Action = function(form, options){
6708     this.form = form;
6709     this.options = options || {};
6710 };
6711 /**
6712  * Client Validation Failed
6713  * @const 
6714  */
6715 Roo.form.Action.CLIENT_INVALID = 'client';
6716 /**
6717  * Server Validation Failed
6718  * @const 
6719  */
6720 Roo.form.Action.SERVER_INVALID = 'server';
6721  /**
6722  * Connect to Server Failed
6723  * @const 
6724  */
6725 Roo.form.Action.CONNECT_FAILURE = 'connect';
6726 /**
6727  * Reading Data from Server Failed
6728  * @const 
6729  */
6730 Roo.form.Action.LOAD_FAILURE = 'load';
6731
6732 Roo.form.Action.prototype = {
6733     type : 'default',
6734     failureType : undefined,
6735     response : undefined,
6736     result : undefined,
6737
6738     // interface method
6739     run : function(options){
6740
6741     },
6742
6743     // interface method
6744     success : function(response){
6745
6746     },
6747
6748     // interface method
6749     handleResponse : function(response){
6750
6751     },
6752
6753     // default connection failure
6754     failure : function(response){
6755         
6756         this.response = response;
6757         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6758         this.form.afterAction(this, false);
6759     },
6760
6761     processResponse : function(response){
6762         this.response = response;
6763         if(!response.responseText){
6764             return true;
6765         }
6766         this.result = this.handleResponse(response);
6767         return this.result;
6768     },
6769
6770     // utility functions used internally
6771     getUrl : function(appendParams){
6772         var url = this.options.url || this.form.url || this.form.el.dom.action;
6773         if(appendParams){
6774             var p = this.getParams();
6775             if(p){
6776                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6777             }
6778         }
6779         return url;
6780     },
6781
6782     getMethod : function(){
6783         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6784     },
6785
6786     getParams : function(){
6787         var bp = this.form.baseParams;
6788         var p = this.options.params;
6789         if(p){
6790             if(typeof p == "object"){
6791                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6792             }else if(typeof p == 'string' && bp){
6793                 p += '&' + Roo.urlEncode(bp);
6794             }
6795         }else if(bp){
6796             p = Roo.urlEncode(bp);
6797         }
6798         return p;
6799     },
6800
6801     createCallback : function(){
6802         return {
6803             success: this.success,
6804             failure: this.failure,
6805             scope: this,
6806             timeout: (this.form.timeout*1000),
6807             upload: this.form.fileUpload ? this.success : undefined
6808         };
6809     }
6810 };
6811
6812 Roo.form.Action.Submit = function(form, options){
6813     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6814 };
6815
6816 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6817     type : 'submit',
6818
6819     haveProgress : false,
6820     uploadComplete : false,
6821     
6822     // uploadProgress indicator.
6823     uploadProgress : function()
6824     {
6825         if (!this.form.progressUrl) {
6826             return;
6827         }
6828         
6829         if (!this.haveProgress) {
6830             Roo.MessageBox.progress("Uploading", "Uploading");
6831         }
6832         if (this.uploadComplete) {
6833            Roo.MessageBox.hide();
6834            return;
6835         }
6836         
6837         this.haveProgress = true;
6838    
6839         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6840         
6841         var c = new Roo.data.Connection();
6842         c.request({
6843             url : this.form.progressUrl,
6844             params: {
6845                 id : uid
6846             },
6847             method: 'GET',
6848             success : function(req){
6849                //console.log(data);
6850                 var rdata = false;
6851                 var edata;
6852                 try  {
6853                    rdata = Roo.decode(req.responseText)
6854                 } catch (e) {
6855                     Roo.log("Invalid data from server..");
6856                     Roo.log(edata);
6857                     return;
6858                 }
6859                 if (!rdata || !rdata.success) {
6860                     Roo.log(rdata);
6861                     Roo.MessageBox.alert(Roo.encode(rdata));
6862                     return;
6863                 }
6864                 var data = rdata.data;
6865                 
6866                 if (this.uploadComplete) {
6867                    Roo.MessageBox.hide();
6868                    return;
6869                 }
6870                    
6871                 if (data){
6872                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6873                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6874                     );
6875                 }
6876                 this.uploadProgress.defer(2000,this);
6877             },
6878        
6879             failure: function(data) {
6880                 Roo.log('progress url failed ');
6881                 Roo.log(data);
6882             },
6883             scope : this
6884         });
6885            
6886     },
6887     
6888     
6889     run : function()
6890     {
6891         // run get Values on the form, so it syncs any secondary forms.
6892         this.form.getValues();
6893         
6894         var o = this.options;
6895         var method = this.getMethod();
6896         var isPost = method == 'POST';
6897         if(o.clientValidation === false || this.form.isValid()){
6898             
6899             if (this.form.progressUrl) {
6900                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6901                     (new Date() * 1) + '' + Math.random());
6902                     
6903             } 
6904             
6905             
6906             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6907                 form:this.form.el.dom,
6908                 url:this.getUrl(!isPost),
6909                 method: method,
6910                 params:isPost ? this.getParams() : null,
6911                 isUpload: this.form.fileUpload
6912             }));
6913             
6914             this.uploadProgress();
6915
6916         }else if (o.clientValidation !== false){ // client validation failed
6917             this.failureType = Roo.form.Action.CLIENT_INVALID;
6918             this.form.afterAction(this, false);
6919         }
6920     },
6921
6922     success : function(response)
6923     {
6924         this.uploadComplete= true;
6925         if (this.haveProgress) {
6926             Roo.MessageBox.hide();
6927         }
6928         
6929         
6930         var result = this.processResponse(response);
6931         if(result === true || result.success){
6932             this.form.afterAction(this, true);
6933             return;
6934         }
6935         if(result.errors){
6936             this.form.markInvalid(result.errors);
6937             this.failureType = Roo.form.Action.SERVER_INVALID;
6938         }
6939         this.form.afterAction(this, false);
6940     },
6941     failure : function(response)
6942     {
6943         this.uploadComplete= true;
6944         if (this.haveProgress) {
6945             Roo.MessageBox.hide();
6946         }
6947         
6948         this.response = response;
6949         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6950         this.form.afterAction(this, false);
6951     },
6952     
6953     handleResponse : function(response){
6954         if(this.form.errorReader){
6955             var rs = this.form.errorReader.read(response);
6956             var errors = [];
6957             if(rs.records){
6958                 for(var i = 0, len = rs.records.length; i < len; i++) {
6959                     var r = rs.records[i];
6960                     errors[i] = r.data;
6961                 }
6962             }
6963             if(errors.length < 1){
6964                 errors = null;
6965             }
6966             return {
6967                 success : rs.success,
6968                 errors : errors
6969             };
6970         }
6971         var ret = false;
6972         try {
6973             ret = Roo.decode(response.responseText);
6974         } catch (e) {
6975             ret = {
6976                 success: false,
6977                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6978                 errors : []
6979             };
6980         }
6981         return ret;
6982         
6983     }
6984 });
6985
6986
6987 Roo.form.Action.Load = function(form, options){
6988     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6989     this.reader = this.form.reader;
6990 };
6991
6992 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6993     type : 'load',
6994
6995     run : function(){
6996         
6997         Roo.Ajax.request(Roo.apply(
6998                 this.createCallback(), {
6999                     method:this.getMethod(),
7000                     url:this.getUrl(false),
7001                     params:this.getParams()
7002         }));
7003     },
7004
7005     success : function(response){
7006         
7007         var result = this.processResponse(response);
7008         if(result === true || !result.success || !result.data){
7009             this.failureType = Roo.form.Action.LOAD_FAILURE;
7010             this.form.afterAction(this, false);
7011             return;
7012         }
7013         this.form.clearInvalid();
7014         this.form.setValues(result.data);
7015         this.form.afterAction(this, true);
7016     },
7017
7018     handleResponse : function(response){
7019         if(this.form.reader){
7020             var rs = this.form.reader.read(response);
7021             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7022             return {
7023                 success : rs.success,
7024                 data : data
7025             };
7026         }
7027         return Roo.decode(response.responseText);
7028     }
7029 });
7030
7031 Roo.form.Action.ACTION_TYPES = {
7032     'load' : Roo.form.Action.Load,
7033     'submit' : Roo.form.Action.Submit
7034 };/*
7035  * - LGPL
7036  *
7037  * form
7038  * 
7039  */
7040
7041 /**
7042  * @class Roo.bootstrap.Form
7043  * @extends Roo.bootstrap.Component
7044  * Bootstrap Form class
7045  * @cfg {String} method  GET | POST (default POST)
7046  * @cfg {String} labelAlign top | left (default top)
7047  * @cfg {String} align left  | right - for navbars
7048  * @cfg {Boolean} loadMask load mask when submit (default true)
7049
7050  * 
7051  * @constructor
7052  * Create a new Form
7053  * @param {Object} config The config object
7054  */
7055
7056
7057 Roo.bootstrap.Form = function(config){
7058     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7059     this.addEvents({
7060         /**
7061          * @event clientvalidation
7062          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7063          * @param {Form} this
7064          * @param {Boolean} valid true if the form has passed client-side validation
7065          */
7066         clientvalidation: true,
7067         /**
7068          * @event beforeaction
7069          * Fires before any action is performed. Return false to cancel the action.
7070          * @param {Form} this
7071          * @param {Action} action The action to be performed
7072          */
7073         beforeaction: true,
7074         /**
7075          * @event actionfailed
7076          * Fires when an action fails.
7077          * @param {Form} this
7078          * @param {Action} action The action that failed
7079          */
7080         actionfailed : true,
7081         /**
7082          * @event actioncomplete
7083          * Fires when an action is completed.
7084          * @param {Form} this
7085          * @param {Action} action The action that completed
7086          */
7087         actioncomplete : true
7088     });
7089     
7090 };
7091
7092 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7093       
7094      /**
7095      * @cfg {String} method
7096      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7097      */
7098     method : 'POST',
7099     /**
7100      * @cfg {String} url
7101      * The URL to use for form actions if one isn't supplied in the action options.
7102      */
7103     /**
7104      * @cfg {Boolean} fileUpload
7105      * Set to true if this form is a file upload.
7106      */
7107      
7108     /**
7109      * @cfg {Object} baseParams
7110      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7111      */
7112       
7113     /**
7114      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7115      */
7116     timeout: 30,
7117     /**
7118      * @cfg {Sting} align (left|right) for navbar forms
7119      */
7120     align : 'left',
7121
7122     // private
7123     activeAction : null,
7124  
7125     /**
7126      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7127      * element by passing it or its id or mask the form itself by passing in true.
7128      * @type Mixed
7129      */
7130     waitMsgTarget : false,
7131     
7132     loadMask : true,
7133     
7134     getAutoCreate : function(){
7135         
7136         var cfg = {
7137             tag: 'form',
7138             method : this.method || 'POST',
7139             id : this.id || Roo.id(),
7140             cls : ''
7141         };
7142         if (this.parent().xtype.match(/^Nav/)) {
7143             cfg.cls = 'navbar-form navbar-' + this.align;
7144             
7145         }
7146         
7147         if (this.labelAlign == 'left' ) {
7148             cfg.cls += ' form-horizontal';
7149         }
7150         
7151         
7152         return cfg;
7153     },
7154     initEvents : function()
7155     {
7156         this.el.on('submit', this.onSubmit, this);
7157         // this was added as random key presses on the form where triggering form submit.
7158         this.el.on('keypress', function(e) {
7159             if (e.getCharCode() != 13) {
7160                 return true;
7161             }
7162             // we might need to allow it for textareas.. and some other items.
7163             // check e.getTarget().
7164             
7165             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7166                 return true;
7167             }
7168         
7169             Roo.log("keypress blocked");
7170             
7171             e.preventDefault();
7172             return false;
7173         });
7174         
7175     },
7176     // private
7177     onSubmit : function(e){
7178         e.stopEvent();
7179     },
7180     
7181      /**
7182      * Returns true if client-side validation on the form is successful.
7183      * @return Boolean
7184      */
7185     isValid : function(){
7186         var items = this.getItems();
7187         var valid = true;
7188         items.each(function(f){
7189            if(!f.validate()){
7190                valid = false;
7191                
7192            }
7193         });
7194         return valid;
7195     },
7196     /**
7197      * Returns true if any fields in this form have changed since their original load.
7198      * @return Boolean
7199      */
7200     isDirty : function(){
7201         var dirty = false;
7202         var items = this.getItems();
7203         items.each(function(f){
7204            if(f.isDirty()){
7205                dirty = true;
7206                return false;
7207            }
7208            return true;
7209         });
7210         return dirty;
7211     },
7212      /**
7213      * Performs a predefined action (submit or load) or custom actions you define on this form.
7214      * @param {String} actionName The name of the action type
7215      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7216      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7217      * accept other config options):
7218      * <pre>
7219 Property          Type             Description
7220 ----------------  ---------------  ----------------------------------------------------------------------------------
7221 url               String           The url for the action (defaults to the form's url)
7222 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7223 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7224 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7225                                    validate the form on the client (defaults to false)
7226      * </pre>
7227      * @return {BasicForm} this
7228      */
7229     doAction : function(action, options){
7230         if(typeof action == 'string'){
7231             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7232         }
7233         if(this.fireEvent('beforeaction', this, action) !== false){
7234             this.beforeAction(action);
7235             action.run.defer(100, action);
7236         }
7237         return this;
7238     },
7239     
7240     // private
7241     beforeAction : function(action){
7242         var o = action.options;
7243         
7244         if(this.loadMask){
7245             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7246         }
7247         // not really supported yet.. ??
7248         
7249         //if(this.waitMsgTarget === true){
7250         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7251         //}else if(this.waitMsgTarget){
7252         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7253         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7254         //}else {
7255         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7256        // }
7257          
7258     },
7259
7260     // private
7261     afterAction : function(action, success){
7262         this.activeAction = null;
7263         var o = action.options;
7264         
7265         //if(this.waitMsgTarget === true){
7266             this.el.unmask();
7267         //}else if(this.waitMsgTarget){
7268         //    this.waitMsgTarget.unmask();
7269         //}else{
7270         //    Roo.MessageBox.updateProgress(1);
7271         //    Roo.MessageBox.hide();
7272        // }
7273         // 
7274         if(success){
7275             if(o.reset){
7276                 this.reset();
7277             }
7278             Roo.callback(o.success, o.scope, [this, action]);
7279             this.fireEvent('actioncomplete', this, action);
7280             
7281         }else{
7282             
7283             // failure condition..
7284             // we have a scenario where updates need confirming.
7285             // eg. if a locking scenario exists..
7286             // we look for { errors : { needs_confirm : true }} in the response.
7287             if (
7288                 (typeof(action.result) != 'undefined')  &&
7289                 (typeof(action.result.errors) != 'undefined')  &&
7290                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7291            ){
7292                 var _t = this;
7293                 Roo.log("not supported yet");
7294                  /*
7295                 
7296                 Roo.MessageBox.confirm(
7297                     "Change requires confirmation",
7298                     action.result.errorMsg,
7299                     function(r) {
7300                         if (r != 'yes') {
7301                             return;
7302                         }
7303                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7304                     }
7305                     
7306                 );
7307                 */
7308                 
7309                 
7310                 return;
7311             }
7312             
7313             Roo.callback(o.failure, o.scope, [this, action]);
7314             // show an error message if no failed handler is set..
7315             if (!this.hasListener('actionfailed')) {
7316                 Roo.log("need to add dialog support");
7317                 /*
7318                 Roo.MessageBox.alert("Error",
7319                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7320                         action.result.errorMsg :
7321                         "Saving Failed, please check your entries or try again"
7322                 );
7323                 */
7324             }
7325             
7326             this.fireEvent('actionfailed', this, action);
7327         }
7328         
7329     },
7330     /**
7331      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7332      * @param {String} id The value to search for
7333      * @return Field
7334      */
7335     findField : function(id){
7336         var items = this.getItems();
7337         var field = items.get(id);
7338         if(!field){
7339              items.each(function(f){
7340                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7341                     field = f;
7342                     return false;
7343                 }
7344                 return true;
7345             });
7346         }
7347         return field || null;
7348     },
7349      /**
7350      * Mark fields in this form invalid in bulk.
7351      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7352      * @return {BasicForm} this
7353      */
7354     markInvalid : function(errors){
7355         if(errors instanceof Array){
7356             for(var i = 0, len = errors.length; i < len; i++){
7357                 var fieldError = errors[i];
7358                 var f = this.findField(fieldError.id);
7359                 if(f){
7360                     f.markInvalid(fieldError.msg);
7361                 }
7362             }
7363         }else{
7364             var field, id;
7365             for(id in errors){
7366                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7367                     field.markInvalid(errors[id]);
7368                 }
7369             }
7370         }
7371         //Roo.each(this.childForms || [], function (f) {
7372         //    f.markInvalid(errors);
7373         //});
7374         
7375         return this;
7376     },
7377
7378     /**
7379      * Set values for fields in this form in bulk.
7380      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7381      * @return {BasicForm} this
7382      */
7383     setValues : function(values){
7384         if(values instanceof Array){ // array of objects
7385             for(var i = 0, len = values.length; i < len; i++){
7386                 var v = values[i];
7387                 var f = this.findField(v.id);
7388                 if(f){
7389                     f.setValue(v.value);
7390                     if(this.trackResetOnLoad){
7391                         f.originalValue = f.getValue();
7392                     }
7393                 }
7394             }
7395         }else{ // object hash
7396             var field, id;
7397             for(id in values){
7398                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7399                     
7400                     if (field.setFromData && 
7401                         field.valueField && 
7402                         field.displayField &&
7403                         // combos' with local stores can 
7404                         // be queried via setValue()
7405                         // to set their value..
7406                         (field.store && !field.store.isLocal)
7407                         ) {
7408                         // it's a combo
7409                         var sd = { };
7410                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7411                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7412                         field.setFromData(sd);
7413                         
7414                     } else {
7415                         field.setValue(values[id]);
7416                     }
7417                     
7418                     
7419                     if(this.trackResetOnLoad){
7420                         field.originalValue = field.getValue();
7421                     }
7422                 }
7423             }
7424         }
7425          
7426         //Roo.each(this.childForms || [], function (f) {
7427         //    f.setValues(values);
7428         //});
7429                 
7430         return this;
7431     },
7432
7433     /**
7434      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7435      * they are returned as an array.
7436      * @param {Boolean} asString
7437      * @return {Object}
7438      */
7439     getValues : function(asString){
7440         //if (this.childForms) {
7441             // copy values from the child forms
7442         //    Roo.each(this.childForms, function (f) {
7443         //        this.setValues(f.getValues());
7444         //    }, this);
7445         //}
7446         
7447         
7448         
7449         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7450         if(asString === true){
7451             return fs;
7452         }
7453         return Roo.urlDecode(fs);
7454     },
7455     
7456     /**
7457      * Returns the fields in this form as an object with key/value pairs. 
7458      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7459      * @return {Object}
7460      */
7461     getFieldValues : function(with_hidden)
7462     {
7463         var items = this.getItems();
7464         var ret = {};
7465         items.each(function(f){
7466             if (!f.getName()) {
7467                 return;
7468             }
7469             var v = f.getValue();
7470             if (f.inputType =='radio') {
7471                 if (typeof(ret[f.getName()]) == 'undefined') {
7472                     ret[f.getName()] = ''; // empty..
7473                 }
7474                 
7475                 if (!f.el.dom.checked) {
7476                     return;
7477                     
7478                 }
7479                 v = f.el.dom.value;
7480                 
7481             }
7482             
7483             // not sure if this supported any more..
7484             if ((typeof(v) == 'object') && f.getRawValue) {
7485                 v = f.getRawValue() ; // dates..
7486             }
7487             // combo boxes where name != hiddenName...
7488             if (f.name != f.getName()) {
7489                 ret[f.name] = f.getRawValue();
7490             }
7491             ret[f.getName()] = v;
7492         });
7493         
7494         return ret;
7495     },
7496
7497     /**
7498      * Clears all invalid messages in this form.
7499      * @return {BasicForm} this
7500      */
7501     clearInvalid : function(){
7502         var items = this.getItems();
7503         
7504         items.each(function(f){
7505            f.clearInvalid();
7506         });
7507         
7508         
7509         
7510         return this;
7511     },
7512
7513     /**
7514      * Resets this form.
7515      * @return {BasicForm} this
7516      */
7517     reset : function(){
7518         var items = this.getItems();
7519         items.each(function(f){
7520             f.reset();
7521         });
7522         
7523         Roo.each(this.childForms || [], function (f) {
7524             f.reset();
7525         });
7526        
7527         
7528         return this;
7529     },
7530     getItems : function()
7531     {
7532         var r=new Roo.util.MixedCollection(false, function(o){
7533             return o.id || (o.id = Roo.id());
7534         });
7535         var iter = function(el) {
7536             if (el.inputEl) {
7537                 r.add(el);
7538             }
7539             if (!el.items) {
7540                 return;
7541             }
7542             Roo.each(el.items,function(e) {
7543                 iter(e);
7544             });
7545             
7546             
7547         };
7548         
7549         iter(this);
7550         return r;
7551         
7552         
7553         
7554         
7555     }
7556     
7557 });
7558
7559  
7560 /*
7561  * Based on:
7562  * Ext JS Library 1.1.1
7563  * Copyright(c) 2006-2007, Ext JS, LLC.
7564  *
7565  * Originally Released Under LGPL - original licence link has changed is not relivant.
7566  *
7567  * Fork - LGPL
7568  * <script type="text/javascript">
7569  */
7570 /**
7571  * @class Roo.form.VTypes
7572  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7573  * @singleton
7574  */
7575 Roo.form.VTypes = function(){
7576     // closure these in so they are only created once.
7577     var alpha = /^[a-zA-Z_]+$/;
7578     var alphanum = /^[a-zA-Z0-9_]+$/;
7579     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7580     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7581
7582     // All these messages and functions are configurable
7583     return {
7584         /**
7585          * The function used to validate email addresses
7586          * @param {String} value The email address
7587          */
7588         'email' : function(v){
7589             return email.test(v);
7590         },
7591         /**
7592          * The error text to display when the email validation function returns false
7593          * @type String
7594          */
7595         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7596         /**
7597          * The keystroke filter mask to be applied on email input
7598          * @type RegExp
7599          */
7600         'emailMask' : /[a-z0-9_\.\-@]/i,
7601
7602         /**
7603          * The function used to validate URLs
7604          * @param {String} value The URL
7605          */
7606         'url' : function(v){
7607             return url.test(v);
7608         },
7609         /**
7610          * The error text to display when the url validation function returns false
7611          * @type String
7612          */
7613         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7614         
7615         /**
7616          * The function used to validate alpha values
7617          * @param {String} value The value
7618          */
7619         'alpha' : function(v){
7620             return alpha.test(v);
7621         },
7622         /**
7623          * The error text to display when the alpha validation function returns false
7624          * @type String
7625          */
7626         'alphaText' : 'This field should only contain letters and _',
7627         /**
7628          * The keystroke filter mask to be applied on alpha input
7629          * @type RegExp
7630          */
7631         'alphaMask' : /[a-z_]/i,
7632
7633         /**
7634          * The function used to validate alphanumeric values
7635          * @param {String} value The value
7636          */
7637         'alphanum' : function(v){
7638             return alphanum.test(v);
7639         },
7640         /**
7641          * The error text to display when the alphanumeric validation function returns false
7642          * @type String
7643          */
7644         'alphanumText' : 'This field should only contain letters, numbers and _',
7645         /**
7646          * The keystroke filter mask to be applied on alphanumeric input
7647          * @type RegExp
7648          */
7649         'alphanumMask' : /[a-z0-9_]/i
7650     };
7651 }();/*
7652  * - LGPL
7653  *
7654  * Input
7655  * 
7656  */
7657
7658 /**
7659  * @class Roo.bootstrap.Input
7660  * @extends Roo.bootstrap.Component
7661  * Bootstrap Input class
7662  * @cfg {Boolean} disabled is it disabled
7663  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7664  * @cfg {String} name name of the input
7665  * @cfg {string} fieldLabel - the label associated
7666  * @cfg {string} placeholder - placeholder to put in text.
7667  * @cfg {string}  before - input group add on before
7668  * @cfg {string} after - input group add on after
7669  * @cfg {string} size - (lg|sm) or leave empty..
7670  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7671  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7672  * @cfg {Number} md colspan out of 12 for computer-sized screens
7673  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7674  * @cfg {string} value default value of the input
7675  * @cfg {Number} labelWidth set the width of label (0-12)
7676  * @cfg {String} labelAlign (top|left)
7677  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7678  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7679
7680  * @cfg {String} align (left|center|right) Default left
7681  * @cfg {Boolean} forceFeedback (true|false) Default false
7682  * 
7683  * 
7684  * 
7685  * 
7686  * @constructor
7687  * Create a new Input
7688  * @param {Object} config The config object
7689  */
7690
7691 Roo.bootstrap.Input = function(config){
7692     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7693    
7694         this.addEvents({
7695             /**
7696              * @event focus
7697              * Fires when this field receives input focus.
7698              * @param {Roo.form.Field} this
7699              */
7700             focus : true,
7701             /**
7702              * @event blur
7703              * Fires when this field loses input focus.
7704              * @param {Roo.form.Field} this
7705              */
7706             blur : true,
7707             /**
7708              * @event specialkey
7709              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7710              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7711              * @param {Roo.form.Field} this
7712              * @param {Roo.EventObject} e The event object
7713              */
7714             specialkey : true,
7715             /**
7716              * @event change
7717              * Fires just before the field blurs if the field value has changed.
7718              * @param {Roo.form.Field} this
7719              * @param {Mixed} newValue The new value
7720              * @param {Mixed} oldValue The original value
7721              */
7722             change : true,
7723             /**
7724              * @event invalid
7725              * Fires after the field has been marked as invalid.
7726              * @param {Roo.form.Field} this
7727              * @param {String} msg The validation message
7728              */
7729             invalid : true,
7730             /**
7731              * @event valid
7732              * Fires after the field has been validated with no errors.
7733              * @param {Roo.form.Field} this
7734              */
7735             valid : true,
7736              /**
7737              * @event keyup
7738              * Fires after the key up
7739              * @param {Roo.form.Field} this
7740              * @param {Roo.EventObject}  e The event Object
7741              */
7742             keyup : true
7743         });
7744 };
7745
7746 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7747      /**
7748      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7749       automatic validation (defaults to "keyup").
7750      */
7751     validationEvent : "keyup",
7752      /**
7753      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7754      */
7755     validateOnBlur : true,
7756     /**
7757      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7758      */
7759     validationDelay : 250,
7760      /**
7761      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7762      */
7763     focusClass : "x-form-focus",  // not needed???
7764     
7765        
7766     /**
7767      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7768      */
7769     invalidClass : "has-warning",
7770     
7771     /**
7772      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7773      */
7774     validClass : "has-success",
7775     
7776     /**
7777      * @cfg {Boolean} hasFeedback (true|false) default true
7778      */
7779     hasFeedback : true,
7780     
7781     /**
7782      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7783      */
7784     invalidFeedbackClass : "glyphicon-warning-sign",
7785     
7786     /**
7787      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7788      */
7789     validFeedbackClass : "glyphicon-ok",
7790     
7791     /**
7792      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7793      */
7794     selectOnFocus : false,
7795     
7796      /**
7797      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7798      */
7799     maskRe : null,
7800        /**
7801      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7802      */
7803     vtype : null,
7804     
7805       /**
7806      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7807      */
7808     disableKeyFilter : false,
7809     
7810        /**
7811      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7812      */
7813     disabled : false,
7814      /**
7815      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7816      */
7817     allowBlank : true,
7818     /**
7819      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7820      */
7821     blankText : "This field is required",
7822     
7823      /**
7824      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7825      */
7826     minLength : 0,
7827     /**
7828      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7829      */
7830     maxLength : Number.MAX_VALUE,
7831     /**
7832      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7833      */
7834     minLengthText : "The minimum length for this field is {0}",
7835     /**
7836      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7837      */
7838     maxLengthText : "The maximum length for this field is {0}",
7839   
7840     
7841     /**
7842      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7843      * If available, this function will be called only after the basic validators all return true, and will be passed the
7844      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7845      */
7846     validator : null,
7847     /**
7848      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7849      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7850      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7851      */
7852     regex : null,
7853     /**
7854      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7855      */
7856     regexText : "",
7857     
7858     autocomplete: false,
7859     
7860     
7861     fieldLabel : '',
7862     inputType : 'text',
7863     
7864     name : false,
7865     placeholder: false,
7866     before : false,
7867     after : false,
7868     size : false,
7869     hasFocus : false,
7870     preventMark: false,
7871     isFormField : true,
7872     value : '',
7873     labelWidth : 2,
7874     labelAlign : false,
7875     readOnly : false,
7876     align : false,
7877     formatedValue : false,
7878     forceFeedback : false,
7879     
7880     parentLabelAlign : function()
7881     {
7882         var parent = this;
7883         while (parent.parent()) {
7884             parent = parent.parent();
7885             if (typeof(parent.labelAlign) !='undefined') {
7886                 return parent.labelAlign;
7887             }
7888         }
7889         return 'left';
7890         
7891     },
7892     
7893     getAutoCreate : function(){
7894         
7895         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7896         
7897         var id = Roo.id();
7898         
7899         var cfg = {};
7900         
7901         if(this.inputType != 'hidden'){
7902             cfg.cls = 'form-group' //input-group
7903         }
7904         
7905         var input =  {
7906             tag: 'input',
7907             id : id,
7908             type : this.inputType,
7909             value : this.value,
7910             cls : 'form-control',
7911             placeholder : this.placeholder || '',
7912             autocomplete : this.autocomplete || 'new-password'
7913         };
7914         
7915         
7916         if(this.align){
7917             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7918         }
7919         
7920         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7921             input.maxLength = this.maxLength;
7922         }
7923         
7924         if (this.disabled) {
7925             input.disabled=true;
7926         }
7927         
7928         if (this.readOnly) {
7929             input.readonly=true;
7930         }
7931         
7932         if (this.name) {
7933             input.name = this.name;
7934         }
7935         if (this.size) {
7936             input.cls += ' input-' + this.size;
7937         }
7938         var settings=this;
7939         ['xs','sm','md','lg'].map(function(size){
7940             if (settings[size]) {
7941                 cfg.cls += ' col-' + size + '-' + settings[size];
7942             }
7943         });
7944         
7945         var inputblock = input;
7946         
7947         var feedback = {
7948             tag: 'span',
7949             cls: 'glyphicon form-control-feedback'
7950         };
7951             
7952         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7953             
7954             inputblock = {
7955                 cls : 'has-feedback',
7956                 cn :  [
7957                     input,
7958                     feedback
7959                 ] 
7960             };  
7961         }
7962         
7963         if (this.before || this.after) {
7964             
7965             inputblock = {
7966                 cls : 'input-group',
7967                 cn :  [] 
7968             };
7969             
7970             if (this.before && typeof(this.before) == 'string') {
7971                 
7972                 inputblock.cn.push({
7973                     tag :'span',
7974                     cls : 'roo-input-before input-group-addon',
7975                     html : this.before
7976                 });
7977             }
7978             if (this.before && typeof(this.before) == 'object') {
7979                 this.before = Roo.factory(this.before);
7980                 
7981                 inputblock.cn.push({
7982                     tag :'span',
7983                     cls : 'roo-input-before input-group-' +
7984                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7985                 });
7986             }
7987             
7988             inputblock.cn.push(input);
7989             
7990             if (this.after && typeof(this.after) == 'string') {
7991                 inputblock.cn.push({
7992                     tag :'span',
7993                     cls : 'roo-input-after input-group-addon',
7994                     html : this.after
7995                 });
7996             }
7997             if (this.after && typeof(this.after) == 'object') {
7998                 this.after = Roo.factory(this.after);
7999                 
8000                 inputblock.cn.push({
8001                     tag :'span',
8002                     cls : 'roo-input-after input-group-' +
8003                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8004                 });
8005             }
8006             
8007             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8008                 inputblock.cls += ' has-feedback';
8009                 inputblock.cn.push(feedback);
8010             }
8011         };
8012         
8013         if (align ==='left' && this.fieldLabel.length) {
8014                 
8015                 cfg.cn = [
8016                     
8017                     {
8018                         tag: 'label',
8019                         'for' :  id,
8020                         cls : 'control-label col-sm-' + this.labelWidth,
8021                         html : this.fieldLabel
8022                         
8023                     },
8024                     {
8025                         cls : "col-sm-" + (12 - this.labelWidth), 
8026                         cn: [
8027                             inputblock
8028                         ]
8029                     }
8030                     
8031                 ];
8032         } else if ( this.fieldLabel.length) {
8033                 
8034                  cfg.cn = [
8035                    
8036                     {
8037                         tag: 'label',
8038                         //cls : 'input-group-addon',
8039                         html : this.fieldLabel
8040                         
8041                     },
8042                     
8043                     inputblock
8044                     
8045                 ];
8046
8047         } else {
8048             
8049                 cfg.cn = [
8050                     
8051                         inputblock
8052                     
8053                 ];
8054                 
8055                 
8056         };
8057         
8058         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8059            cfg.cls += ' navbar-form';
8060         }
8061         
8062         return cfg;
8063         
8064     },
8065     /**
8066      * return the real input element.
8067      */
8068     inputEl: function ()
8069     {
8070         return this.el.select('input.form-control',true).first();
8071     },
8072     
8073     tooltipEl : function()
8074     {
8075         return this.inputEl();
8076     },
8077     
8078     setDisabled : function(v)
8079     {
8080         var i  = this.inputEl().dom;
8081         if (!v) {
8082             i.removeAttribute('disabled');
8083             return;
8084             
8085         }
8086         i.setAttribute('disabled','true');
8087     },
8088     initEvents : function()
8089     {
8090           
8091         this.inputEl().on("keydown" , this.fireKey,  this);
8092         this.inputEl().on("focus", this.onFocus,  this);
8093         this.inputEl().on("blur", this.onBlur,  this);
8094         
8095         this.inputEl().relayEvent('keyup', this);
8096  
8097         // reference to original value for reset
8098         this.originalValue = this.getValue();
8099         //Roo.form.TextField.superclass.initEvents.call(this);
8100         if(this.validationEvent == 'keyup'){
8101             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8102             this.inputEl().on('keyup', this.filterValidation, this);
8103         }
8104         else if(this.validationEvent !== false){
8105             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8106         }
8107         
8108         if(this.selectOnFocus){
8109             this.on("focus", this.preFocus, this);
8110             
8111         }
8112         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8113             this.inputEl().on("keypress", this.filterKeys, this);
8114         }
8115        /* if(this.grow){
8116             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8117             this.el.on("click", this.autoSize,  this);
8118         }
8119         */
8120         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8121             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8122         }
8123         
8124         if (typeof(this.before) == 'object') {
8125             this.before.render(this.el.select('.roo-input-before',true).first());
8126         }
8127         if (typeof(this.after) == 'object') {
8128             this.after.render(this.el.select('.roo-input-after',true).first());
8129         }
8130         
8131         
8132     },
8133     filterValidation : function(e){
8134         if(!e.isNavKeyPress()){
8135             this.validationTask.delay(this.validationDelay);
8136         }
8137     },
8138      /**
8139      * Validates the field value
8140      * @return {Boolean} True if the value is valid, else false
8141      */
8142     validate : function(){
8143         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8144         if(this.disabled || this.validateValue(this.getRawValue())){
8145             this.markValid();
8146             return true;
8147         }
8148         
8149         this.markInvalid();
8150         return false;
8151     },
8152     
8153     
8154     /**
8155      * Validates a value according to the field's validation rules and marks the field as invalid
8156      * if the validation fails
8157      * @param {Mixed} value The value to validate
8158      * @return {Boolean} True if the value is valid, else false
8159      */
8160     validateValue : function(value){
8161         if(value.length < 1)  { // if it's blank
8162             if(this.allowBlank){
8163                 return true;
8164             }
8165             return false;
8166         }
8167         
8168         if(value.length < this.minLength){
8169             return false;
8170         }
8171         if(value.length > this.maxLength){
8172             return false;
8173         }
8174         if(this.vtype){
8175             var vt = Roo.form.VTypes;
8176             if(!vt[this.vtype](value, this)){
8177                 return false;
8178             }
8179         }
8180         if(typeof this.validator == "function"){
8181             var msg = this.validator(value);
8182             if(msg !== true){
8183                 return false;
8184             }
8185         }
8186         
8187         if(this.regex && !this.regex.test(value)){
8188             return false;
8189         }
8190         
8191         return true;
8192     },
8193
8194     
8195     
8196      // private
8197     fireKey : function(e){
8198         //Roo.log('field ' + e.getKey());
8199         if(e.isNavKeyPress()){
8200             this.fireEvent("specialkey", this, e);
8201         }
8202     },
8203     focus : function (selectText){
8204         if(this.rendered){
8205             this.inputEl().focus();
8206             if(selectText === true){
8207                 this.inputEl().dom.select();
8208             }
8209         }
8210         return this;
8211     } ,
8212     
8213     onFocus : function(){
8214         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8215            // this.el.addClass(this.focusClass);
8216         }
8217         if(!this.hasFocus){
8218             this.hasFocus = true;
8219             this.startValue = this.getValue();
8220             this.fireEvent("focus", this);
8221         }
8222     },
8223     
8224     beforeBlur : Roo.emptyFn,
8225
8226     
8227     // private
8228     onBlur : function(){
8229         this.beforeBlur();
8230         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8231             //this.el.removeClass(this.focusClass);
8232         }
8233         this.hasFocus = false;
8234         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8235             this.validate();
8236         }
8237         var v = this.getValue();
8238         if(String(v) !== String(this.startValue)){
8239             this.fireEvent('change', this, v, this.startValue);
8240         }
8241         this.fireEvent("blur", this);
8242     },
8243     
8244     /**
8245      * Resets the current field value to the originally loaded value and clears any validation messages
8246      */
8247     reset : function(){
8248         this.setValue(this.originalValue);
8249         this.validate();
8250     },
8251      /**
8252      * Returns the name of the field
8253      * @return {Mixed} name The name field
8254      */
8255     getName: function(){
8256         return this.name;
8257     },
8258      /**
8259      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8260      * @return {Mixed} value The field value
8261      */
8262     getValue : function(){
8263         
8264         var v = this.inputEl().getValue();
8265         
8266         return v;
8267     },
8268     /**
8269      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8270      * @return {Mixed} value The field value
8271      */
8272     getRawValue : function(){
8273         var v = this.inputEl().getValue();
8274         
8275         return v;
8276     },
8277     
8278     /**
8279      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8280      * @param {Mixed} value The value to set
8281      */
8282     setRawValue : function(v){
8283         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8284     },
8285     
8286     selectText : function(start, end){
8287         var v = this.getRawValue();
8288         if(v.length > 0){
8289             start = start === undefined ? 0 : start;
8290             end = end === undefined ? v.length : end;
8291             var d = this.inputEl().dom;
8292             if(d.setSelectionRange){
8293                 d.setSelectionRange(start, end);
8294             }else if(d.createTextRange){
8295                 var range = d.createTextRange();
8296                 range.moveStart("character", start);
8297                 range.moveEnd("character", v.length-end);
8298                 range.select();
8299             }
8300         }
8301     },
8302     
8303     /**
8304      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8305      * @param {Mixed} value The value to set
8306      */
8307     setValue : function(v){
8308         this.value = v;
8309         if(this.rendered){
8310             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8311             this.validate();
8312         }
8313     },
8314     
8315     /*
8316     processValue : function(value){
8317         if(this.stripCharsRe){
8318             var newValue = value.replace(this.stripCharsRe, '');
8319             if(newValue !== value){
8320                 this.setRawValue(newValue);
8321                 return newValue;
8322             }
8323         }
8324         return value;
8325     },
8326   */
8327     preFocus : function(){
8328         
8329         if(this.selectOnFocus){
8330             this.inputEl().dom.select();
8331         }
8332     },
8333     filterKeys : function(e){
8334         var k = e.getKey();
8335         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8336             return;
8337         }
8338         var c = e.getCharCode(), cc = String.fromCharCode(c);
8339         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8340             return;
8341         }
8342         if(!this.maskRe.test(cc)){
8343             e.stopEvent();
8344         }
8345     },
8346      /**
8347      * Clear any invalid styles/messages for this field
8348      */
8349     clearInvalid : function(){
8350         
8351         if(!this.el || this.preventMark){ // not rendered
8352             return;
8353         }
8354         this.el.removeClass(this.invalidClass);
8355         
8356         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8357             
8358             var feedback = this.el.select('.form-control-feedback', true).first();
8359             
8360             if(feedback){
8361                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8362             }
8363             
8364         }
8365         
8366         this.fireEvent('valid', this);
8367     },
8368     
8369      /**
8370      * Mark this field as valid
8371      */
8372     markValid : function()
8373     {
8374         if(!this.el  || this.preventMark){ // not rendered
8375             return;
8376         }
8377         
8378         this.el.removeClass([this.invalidClass, this.validClass]);
8379         
8380         var feedback = this.el.select('.form-control-feedback', true).first();
8381             
8382         if(feedback){
8383             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8384         }
8385
8386         if(this.disabled || this.allowBlank){
8387             return;
8388         }
8389         
8390         var formGroup = this.el.findParent('.form-group', false, true);
8391         
8392         if(formGroup){
8393             
8394             var label = formGroup.select('label', true).first();
8395             var icon = formGroup.select('i.fa-star', true).first();
8396             
8397             if(label && icon){
8398                 icon.remove();
8399             }
8400         }
8401         
8402         this.el.addClass(this.validClass);
8403         
8404         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8405             
8406             var feedback = this.el.select('.form-control-feedback', true).first();
8407             
8408             if(feedback){
8409                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8410                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8411             }
8412             
8413         }
8414         
8415         this.fireEvent('valid', this);
8416     },
8417     
8418      /**
8419      * Mark this field as invalid
8420      * @param {String} msg The validation message
8421      */
8422     markInvalid : function(msg)
8423     {
8424         if(!this.el  || this.preventMark){ // not rendered
8425             return;
8426         }
8427         
8428         this.el.removeClass([this.invalidClass, this.validClass]);
8429         
8430         var feedback = this.el.select('.form-control-feedback', true).first();
8431             
8432         if(feedback){
8433             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8434         }
8435
8436         if(this.disabled || this.allowBlank){
8437             return;
8438         }
8439         
8440         var formGroup = this.el.findParent('.form-group', false, true);
8441         
8442         if(formGroup){
8443             var label = formGroup.select('label', true).first();
8444             var icon = formGroup.select('i.fa-star', true).first();
8445
8446             if(!this.getValue().length && label && !icon){
8447                 this.el.findParent('.form-group', false, true).createChild({
8448                     tag : 'i',
8449                     cls : 'text-danger fa fa-lg fa-star',
8450                     tooltip : 'This field is required',
8451                     style : 'margin-right:5px;'
8452                 }, label, true);
8453             }
8454         }
8455         
8456         
8457         this.el.addClass(this.invalidClass);
8458         
8459         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8460             
8461             var feedback = this.el.select('.form-control-feedback', true).first();
8462             
8463             if(feedback){
8464                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8465                 
8466                 if(this.getValue().length || this.forceFeedback){
8467                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8468                 }
8469                 
8470             }
8471             
8472         }
8473         
8474         this.fireEvent('invalid', this, msg);
8475     },
8476     // private
8477     SafariOnKeyDown : function(event)
8478     {
8479         // this is a workaround for a password hang bug on chrome/ webkit.
8480         
8481         var isSelectAll = false;
8482         
8483         if(this.inputEl().dom.selectionEnd > 0){
8484             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8485         }
8486         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8487             event.preventDefault();
8488             this.setValue('');
8489             return;
8490         }
8491         
8492         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8493             
8494             event.preventDefault();
8495             // this is very hacky as keydown always get's upper case.
8496             //
8497             var cc = String.fromCharCode(event.getCharCode());
8498             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8499             
8500         }
8501     },
8502     adjustWidth : function(tag, w){
8503         tag = tag.toLowerCase();
8504         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8505             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8506                 if(tag == 'input'){
8507                     return w + 2;
8508                 }
8509                 if(tag == 'textarea'){
8510                     return w-2;
8511                 }
8512             }else if(Roo.isOpera){
8513                 if(tag == 'input'){
8514                     return w + 2;
8515                 }
8516                 if(tag == 'textarea'){
8517                     return w-2;
8518                 }
8519             }
8520         }
8521         return w;
8522     }
8523     
8524 });
8525
8526  
8527 /*
8528  * - LGPL
8529  *
8530  * Input
8531  * 
8532  */
8533
8534 /**
8535  * @class Roo.bootstrap.TextArea
8536  * @extends Roo.bootstrap.Input
8537  * Bootstrap TextArea class
8538  * @cfg {Number} cols Specifies the visible width of a text area
8539  * @cfg {Number} rows Specifies the visible number of lines in a text area
8540  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8541  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8542  * @cfg {string} html text
8543  * 
8544  * @constructor
8545  * Create a new TextArea
8546  * @param {Object} config The config object
8547  */
8548
8549 Roo.bootstrap.TextArea = function(config){
8550     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8551    
8552 };
8553
8554 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8555      
8556     cols : false,
8557     rows : 5,
8558     readOnly : false,
8559     warp : 'soft',
8560     resize : false,
8561     value: false,
8562     html: false,
8563     
8564     getAutoCreate : function(){
8565         
8566         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8567         
8568         var id = Roo.id();
8569         
8570         var cfg = {};
8571         
8572         var input =  {
8573             tag: 'textarea',
8574             id : id,
8575             warp : this.warp,
8576             rows : this.rows,
8577             value : this.value || '',
8578             html: this.html || '',
8579             cls : 'form-control',
8580             placeholder : this.placeholder || '' 
8581             
8582         };
8583         
8584         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8585             input.maxLength = this.maxLength;
8586         }
8587         
8588         if(this.resize){
8589             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8590         }
8591         
8592         if(this.cols){
8593             input.cols = this.cols;
8594         }
8595         
8596         if (this.readOnly) {
8597             input.readonly = true;
8598         }
8599         
8600         if (this.name) {
8601             input.name = this.name;
8602         }
8603         
8604         if (this.size) {
8605             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8606         }
8607         
8608         var settings=this;
8609         ['xs','sm','md','lg'].map(function(size){
8610             if (settings[size]) {
8611                 cfg.cls += ' col-' + size + '-' + settings[size];
8612             }
8613         });
8614         
8615         var inputblock = input;
8616         
8617         if(this.hasFeedback && !this.allowBlank){
8618             
8619             var feedback = {
8620                 tag: 'span',
8621                 cls: 'glyphicon form-control-feedback'
8622             };
8623
8624             inputblock = {
8625                 cls : 'has-feedback',
8626                 cn :  [
8627                     input,
8628                     feedback
8629                 ] 
8630             };  
8631         }
8632         
8633         
8634         if (this.before || this.after) {
8635             
8636             inputblock = {
8637                 cls : 'input-group',
8638                 cn :  [] 
8639             };
8640             if (this.before) {
8641                 inputblock.cn.push({
8642                     tag :'span',
8643                     cls : 'input-group-addon',
8644                     html : this.before
8645                 });
8646             }
8647             
8648             inputblock.cn.push(input);
8649             
8650             if(this.hasFeedback && !this.allowBlank){
8651                 inputblock.cls += ' has-feedback';
8652                 inputblock.cn.push(feedback);
8653             }
8654             
8655             if (this.after) {
8656                 inputblock.cn.push({
8657                     tag :'span',
8658                     cls : 'input-group-addon',
8659                     html : this.after
8660                 });
8661             }
8662             
8663         }
8664         
8665         if (align ==='left' && this.fieldLabel.length) {
8666 //                Roo.log("left and has label");
8667                 cfg.cn = [
8668                     
8669                     {
8670                         tag: 'label',
8671                         'for' :  id,
8672                         cls : 'control-label col-sm-' + this.labelWidth,
8673                         html : this.fieldLabel
8674                         
8675                     },
8676                     {
8677                         cls : "col-sm-" + (12 - this.labelWidth), 
8678                         cn: [
8679                             inputblock
8680                         ]
8681                     }
8682                     
8683                 ];
8684         } else if ( this.fieldLabel.length) {
8685 //                Roo.log(" label");
8686                  cfg.cn = [
8687                    
8688                     {
8689                         tag: 'label',
8690                         //cls : 'input-group-addon',
8691                         html : this.fieldLabel
8692                         
8693                     },
8694                     
8695                     inputblock
8696                     
8697                 ];
8698
8699         } else {
8700             
8701 //                   Roo.log(" no label && no align");
8702                 cfg.cn = [
8703                     
8704                         inputblock
8705                     
8706                 ];
8707                 
8708                 
8709         }
8710         
8711         if (this.disabled) {
8712             input.disabled=true;
8713         }
8714         
8715         return cfg;
8716         
8717     },
8718     /**
8719      * return the real textarea element.
8720      */
8721     inputEl: function ()
8722     {
8723         return this.el.select('textarea.form-control',true).first();
8724     },
8725     
8726     /**
8727      * Clear any invalid styles/messages for this field
8728      */
8729     clearInvalid : function()
8730     {
8731         
8732         if(!this.el || this.preventMark){ // not rendered
8733             return;
8734         }
8735         
8736         var label = this.el.select('label', true).first();
8737         var icon = this.el.select('i.fa-star', true).first();
8738         
8739         if(label && icon){
8740             icon.remove();
8741         }
8742         
8743         this.el.removeClass(this.invalidClass);
8744         
8745         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8746             
8747             var feedback = this.el.select('.form-control-feedback', true).first();
8748             
8749             if(feedback){
8750                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8751             }
8752             
8753         }
8754         
8755         this.fireEvent('valid', this);
8756     },
8757     
8758      /**
8759      * Mark this field as valid
8760      */
8761     markValid : function()
8762     {
8763         if(!this.el  || this.preventMark){ // not rendered
8764             return;
8765         }
8766         
8767         this.el.removeClass([this.invalidClass, this.validClass]);
8768         
8769         var feedback = this.el.select('.form-control-feedback', true).first();
8770             
8771         if(feedback){
8772             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8773         }
8774
8775         if(this.disabled || this.allowBlank){
8776             return;
8777         }
8778         
8779         var label = this.el.select('label', true).first();
8780         var icon = this.el.select('i.fa-star', true).first();
8781         
8782         if(label && icon){
8783             icon.remove();
8784         }
8785         
8786         this.el.addClass(this.validClass);
8787         
8788         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8789             
8790             var feedback = this.el.select('.form-control-feedback', true).first();
8791             
8792             if(feedback){
8793                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8794                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8795             }
8796             
8797         }
8798         
8799         this.fireEvent('valid', this);
8800     },
8801     
8802      /**
8803      * Mark this field as invalid
8804      * @param {String} msg The validation message
8805      */
8806     markInvalid : function(msg)
8807     {
8808         if(!this.el  || this.preventMark){ // not rendered
8809             return;
8810         }
8811         
8812         this.el.removeClass([this.invalidClass, this.validClass]);
8813         
8814         var feedback = this.el.select('.form-control-feedback', true).first();
8815             
8816         if(feedback){
8817             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8818         }
8819
8820         if(this.disabled || this.allowBlank){
8821             return;
8822         }
8823         
8824         var label = this.el.select('label', true).first();
8825         var icon = this.el.select('i.fa-star', true).first();
8826         
8827         if(!this.getValue().length && label && !icon){
8828             this.el.createChild({
8829                 tag : 'i',
8830                 cls : 'text-danger fa fa-lg fa-star',
8831                 tooltip : 'This field is required',
8832                 style : 'margin-right:5px;'
8833             }, label, true);
8834         }
8835
8836         this.el.addClass(this.invalidClass);
8837         
8838         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8839             
8840             var feedback = this.el.select('.form-control-feedback', true).first();
8841             
8842             if(feedback){
8843                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8844                 
8845                 if(this.getValue().length || this.forceFeedback){
8846                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8847                 }
8848                 
8849             }
8850             
8851         }
8852         
8853         this.fireEvent('invalid', this, msg);
8854     }
8855 });
8856
8857  
8858 /*
8859  * - LGPL
8860  *
8861  * trigger field - base class for combo..
8862  * 
8863  */
8864  
8865 /**
8866  * @class Roo.bootstrap.TriggerField
8867  * @extends Roo.bootstrap.Input
8868  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8869  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8870  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8871  * for which you can provide a custom implementation.  For example:
8872  * <pre><code>
8873 var trigger = new Roo.bootstrap.TriggerField();
8874 trigger.onTriggerClick = myTriggerFn;
8875 trigger.applyTo('my-field');
8876 </code></pre>
8877  *
8878  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8879  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8880  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8881  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8882  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8883
8884  * @constructor
8885  * Create a new TriggerField.
8886  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8887  * to the base TextField)
8888  */
8889 Roo.bootstrap.TriggerField = function(config){
8890     this.mimicing = false;
8891     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8892 };
8893
8894 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8895     /**
8896      * @cfg {String} triggerClass A CSS class to apply to the trigger
8897      */
8898      /**
8899      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8900      */
8901     hideTrigger:false,
8902
8903     /**
8904      * @cfg {Boolean} removable (true|false) special filter default false
8905      */
8906     removable : false,
8907     
8908     /** @cfg {Boolean} grow @hide */
8909     /** @cfg {Number} growMin @hide */
8910     /** @cfg {Number} growMax @hide */
8911
8912     /**
8913      * @hide 
8914      * @method
8915      */
8916     autoSize: Roo.emptyFn,
8917     // private
8918     monitorTab : true,
8919     // private
8920     deferHeight : true,
8921
8922     
8923     actionMode : 'wrap',
8924     
8925     caret : false,
8926     
8927     
8928     getAutoCreate : function(){
8929        
8930         var align = this.labelAlign || this.parentLabelAlign();
8931         
8932         var id = Roo.id();
8933         
8934         var cfg = {
8935             cls: 'form-group' //input-group
8936         };
8937         
8938         
8939         var input =  {
8940             tag: 'input',
8941             id : id,
8942             type : this.inputType,
8943             cls : 'form-control',
8944             autocomplete: 'new-password',
8945             placeholder : this.placeholder || '' 
8946             
8947         };
8948         if (this.name) {
8949             input.name = this.name;
8950         }
8951         if (this.size) {
8952             input.cls += ' input-' + this.size;
8953         }
8954         
8955         if (this.disabled) {
8956             input.disabled=true;
8957         }
8958         
8959         var inputblock = input;
8960         
8961         if(this.hasFeedback && !this.allowBlank){
8962             
8963             var feedback = {
8964                 tag: 'span',
8965                 cls: 'glyphicon form-control-feedback'
8966             };
8967             
8968             if(this.removable && !this.editable && !this.tickable){
8969                 inputblock = {
8970                     cls : 'has-feedback',
8971                     cn :  [
8972                         inputblock,
8973                         {
8974                             tag: 'button',
8975                             html : 'x',
8976                             cls : 'roo-combo-removable-btn close'
8977                         },
8978                         feedback
8979                     ] 
8980                 };
8981             } else {
8982                 inputblock = {
8983                     cls : 'has-feedback',
8984                     cn :  [
8985                         inputblock,
8986                         feedback
8987                     ] 
8988                 };
8989             }
8990
8991         } else {
8992             if(this.removable && !this.editable && !this.tickable){
8993                 inputblock = {
8994                     cls : 'roo-removable',
8995                     cn :  [
8996                         inputblock,
8997                         {
8998                             tag: 'button',
8999                             html : 'x',
9000                             cls : 'roo-combo-removable-btn close'
9001                         }
9002                     ] 
9003                 };
9004             }
9005         }
9006         
9007         if (this.before || this.after) {
9008             
9009             inputblock = {
9010                 cls : 'input-group',
9011                 cn :  [] 
9012             };
9013             if (this.before) {
9014                 inputblock.cn.push({
9015                     tag :'span',
9016                     cls : 'input-group-addon',
9017                     html : this.before
9018                 });
9019             }
9020             
9021             inputblock.cn.push(input);
9022             
9023             if(this.hasFeedback && !this.allowBlank){
9024                 inputblock.cls += ' has-feedback';
9025                 inputblock.cn.push(feedback);
9026             }
9027             
9028             if (this.after) {
9029                 inputblock.cn.push({
9030                     tag :'span',
9031                     cls : 'input-group-addon',
9032                     html : this.after
9033                 });
9034             }
9035             
9036         };
9037         
9038         var box = {
9039             tag: 'div',
9040             cn: [
9041                 {
9042                     tag: 'input',
9043                     type : 'hidden',
9044                     cls: 'form-hidden-field'
9045                 },
9046                 inputblock
9047             ]
9048             
9049         };
9050         
9051         if(this.multiple){
9052             box = {
9053                 tag: 'div',
9054                 cn: [
9055                     {
9056                         tag: 'input',
9057                         type : 'hidden',
9058                         cls: 'form-hidden-field'
9059                     },
9060                     {
9061                         tag: 'ul',
9062                         cls: 'select2-choices',
9063                         cn:[
9064                             {
9065                                 tag: 'li',
9066                                 cls: 'select2-search-field',
9067                                 cn: [
9068
9069                                     inputblock
9070                                 ]
9071                             }
9072                         ]
9073                     }
9074                 ]
9075             }
9076         };
9077         
9078         var combobox = {
9079             cls: 'select2-container input-group',
9080             cn: [
9081                 box
9082 //                {
9083 //                    tag: 'ul',
9084 //                    cls: 'typeahead typeahead-long dropdown-menu',
9085 //                    style: 'display:none'
9086 //                }
9087             ]
9088         };
9089         
9090         if(!this.multiple && this.showToggleBtn){
9091             
9092             var caret = {
9093                         tag: 'span',
9094                         cls: 'caret'
9095              };
9096             if (this.caret != false) {
9097                 caret = {
9098                      tag: 'i',
9099                      cls: 'fa fa-' + this.caret
9100                 };
9101                 
9102             }
9103             
9104             combobox.cn.push({
9105                 tag :'span',
9106                 cls : 'input-group-addon btn dropdown-toggle',
9107                 cn : [
9108                     caret,
9109                     {
9110                         tag: 'span',
9111                         cls: 'combobox-clear',
9112                         cn  : [
9113                             {
9114                                 tag : 'i',
9115                                 cls: 'icon-remove'
9116                             }
9117                         ]
9118                     }
9119                 ]
9120
9121             })
9122         }
9123         
9124         if(this.multiple){
9125             combobox.cls += ' select2-container-multi';
9126         }
9127         
9128         if (align ==='left' && this.fieldLabel.length) {
9129             
9130 //                Roo.log("left and has label");
9131                 cfg.cn = [
9132                     
9133                     {
9134                         tag: 'label',
9135                         'for' :  id,
9136                         cls : 'control-label col-sm-' + this.labelWidth,
9137                         html : this.fieldLabel
9138                         
9139                     },
9140                     {
9141                         cls : "col-sm-" + (12 - this.labelWidth), 
9142                         cn: [
9143                             combobox
9144                         ]
9145                     }
9146                     
9147                 ];
9148         } else if ( this.fieldLabel.length) {
9149 //                Roo.log(" label");
9150                  cfg.cn = [
9151                    
9152                     {
9153                         tag: 'label',
9154                         //cls : 'input-group-addon',
9155                         html : this.fieldLabel
9156                         
9157                     },
9158                     
9159                     combobox
9160                     
9161                 ];
9162
9163         } else {
9164             
9165 //                Roo.log(" no label && no align");
9166                 cfg = combobox
9167                      
9168                 
9169         }
9170          
9171         var settings=this;
9172         ['xs','sm','md','lg'].map(function(size){
9173             if (settings[size]) {
9174                 cfg.cls += ' col-' + size + '-' + settings[size];
9175             }
9176         });
9177         
9178         return cfg;
9179         
9180     },
9181     
9182     
9183     
9184     // private
9185     onResize : function(w, h){
9186 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9187 //        if(typeof w == 'number'){
9188 //            var x = w - this.trigger.getWidth();
9189 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9190 //            this.trigger.setStyle('left', x+'px');
9191 //        }
9192     },
9193
9194     // private
9195     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9196
9197     // private
9198     getResizeEl : function(){
9199         return this.inputEl();
9200     },
9201
9202     // private
9203     getPositionEl : function(){
9204         return this.inputEl();
9205     },
9206
9207     // private
9208     alignErrorIcon : function(){
9209         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9210     },
9211
9212     // private
9213     initEvents : function(){
9214         
9215         this.createList();
9216         
9217         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9218         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9219         if(!this.multiple && this.showToggleBtn){
9220             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9221             if(this.hideTrigger){
9222                 this.trigger.setDisplayed(false);
9223             }
9224             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9225         }
9226         
9227         if(this.multiple){
9228             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9229         }
9230         
9231         if(this.removable && !this.editable && !this.tickable){
9232             var close = this.closeTriggerEl();
9233             
9234             if(close){
9235                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9236                 close.on('click', this.removeBtnClick, this, close);
9237             }
9238         }
9239         
9240         //this.trigger.addClassOnOver('x-form-trigger-over');
9241         //this.trigger.addClassOnClick('x-form-trigger-click');
9242         
9243         //if(!this.width){
9244         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9245         //}
9246     },
9247     
9248     closeTriggerEl : function()
9249     {
9250         var close = this.el.select('.roo-combo-removable-btn', true).first();
9251         return close ? close : false;
9252     },
9253     
9254     removeBtnClick : function(e, h, el)
9255     {
9256         e.preventDefault();
9257         
9258         if(this.fireEvent("remove", this) !== false){
9259             this.reset();
9260         }
9261     },
9262     
9263     createList : function()
9264     {
9265         this.list = Roo.get(document.body).createChild({
9266             tag: 'ul',
9267             cls: 'typeahead typeahead-long dropdown-menu',
9268             style: 'display:none'
9269         });
9270         
9271         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9272         
9273     },
9274
9275     // private
9276     initTrigger : function(){
9277        
9278     },
9279
9280     // private
9281     onDestroy : function(){
9282         if(this.trigger){
9283             this.trigger.removeAllListeners();
9284           //  this.trigger.remove();
9285         }
9286         //if(this.wrap){
9287         //    this.wrap.remove();
9288         //}
9289         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9290     },
9291
9292     // private
9293     onFocus : function(){
9294         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9295         /*
9296         if(!this.mimicing){
9297             this.wrap.addClass('x-trigger-wrap-focus');
9298             this.mimicing = true;
9299             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9300             if(this.monitorTab){
9301                 this.el.on("keydown", this.checkTab, this);
9302             }
9303         }
9304         */
9305     },
9306
9307     // private
9308     checkTab : function(e){
9309         if(e.getKey() == e.TAB){
9310             this.triggerBlur();
9311         }
9312     },
9313
9314     // private
9315     onBlur : function(){
9316         // do nothing
9317     },
9318
9319     // private
9320     mimicBlur : function(e, t){
9321         /*
9322         if(!this.wrap.contains(t) && this.validateBlur()){
9323             this.triggerBlur();
9324         }
9325         */
9326     },
9327
9328     // private
9329     triggerBlur : function(){
9330         this.mimicing = false;
9331         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9332         if(this.monitorTab){
9333             this.el.un("keydown", this.checkTab, this);
9334         }
9335         //this.wrap.removeClass('x-trigger-wrap-focus');
9336         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9337     },
9338
9339     // private
9340     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9341     validateBlur : function(e, t){
9342         return true;
9343     },
9344
9345     // private
9346     onDisable : function(){
9347         this.inputEl().dom.disabled = true;
9348         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9349         //if(this.wrap){
9350         //    this.wrap.addClass('x-item-disabled');
9351         //}
9352     },
9353
9354     // private
9355     onEnable : function(){
9356         this.inputEl().dom.disabled = false;
9357         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9358         //if(this.wrap){
9359         //    this.el.removeClass('x-item-disabled');
9360         //}
9361     },
9362
9363     // private
9364     onShow : function(){
9365         var ae = this.getActionEl();
9366         
9367         if(ae){
9368             ae.dom.style.display = '';
9369             ae.dom.style.visibility = 'visible';
9370         }
9371     },
9372
9373     // private
9374     
9375     onHide : function(){
9376         var ae = this.getActionEl();
9377         ae.dom.style.display = 'none';
9378     },
9379
9380     /**
9381      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9382      * by an implementing function.
9383      * @method
9384      * @param {EventObject} e
9385      */
9386     onTriggerClick : Roo.emptyFn
9387 });
9388  /*
9389  * Based on:
9390  * Ext JS Library 1.1.1
9391  * Copyright(c) 2006-2007, Ext JS, LLC.
9392  *
9393  * Originally Released Under LGPL - original licence link has changed is not relivant.
9394  *
9395  * Fork - LGPL
9396  * <script type="text/javascript">
9397  */
9398
9399
9400 /**
9401  * @class Roo.data.SortTypes
9402  * @singleton
9403  * Defines the default sorting (casting?) comparison functions used when sorting data.
9404  */
9405 Roo.data.SortTypes = {
9406     /**
9407      * Default sort that does nothing
9408      * @param {Mixed} s The value being converted
9409      * @return {Mixed} The comparison value
9410      */
9411     none : function(s){
9412         return s;
9413     },
9414     
9415     /**
9416      * The regular expression used to strip tags
9417      * @type {RegExp}
9418      * @property
9419      */
9420     stripTagsRE : /<\/?[^>]+>/gi,
9421     
9422     /**
9423      * Strips all HTML tags to sort on text only
9424      * @param {Mixed} s The value being converted
9425      * @return {String} The comparison value
9426      */
9427     asText : function(s){
9428         return String(s).replace(this.stripTagsRE, "");
9429     },
9430     
9431     /**
9432      * Strips all HTML tags to sort on text only - Case insensitive
9433      * @param {Mixed} s The value being converted
9434      * @return {String} The comparison value
9435      */
9436     asUCText : function(s){
9437         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9438     },
9439     
9440     /**
9441      * Case insensitive string
9442      * @param {Mixed} s The value being converted
9443      * @return {String} The comparison value
9444      */
9445     asUCString : function(s) {
9446         return String(s).toUpperCase();
9447     },
9448     
9449     /**
9450      * Date sorting
9451      * @param {Mixed} s The value being converted
9452      * @return {Number} The comparison value
9453      */
9454     asDate : function(s) {
9455         if(!s){
9456             return 0;
9457         }
9458         if(s instanceof Date){
9459             return s.getTime();
9460         }
9461         return Date.parse(String(s));
9462     },
9463     
9464     /**
9465      * Float sorting
9466      * @param {Mixed} s The value being converted
9467      * @return {Float} The comparison value
9468      */
9469     asFloat : function(s) {
9470         var val = parseFloat(String(s).replace(/,/g, ""));
9471         if(isNaN(val)) {
9472             val = 0;
9473         }
9474         return val;
9475     },
9476     
9477     /**
9478      * Integer sorting
9479      * @param {Mixed} s The value being converted
9480      * @return {Number} The comparison value
9481      */
9482     asInt : function(s) {
9483         var val = parseInt(String(s).replace(/,/g, ""));
9484         if(isNaN(val)) {
9485             val = 0;
9486         }
9487         return val;
9488     }
9489 };/*
9490  * Based on:
9491  * Ext JS Library 1.1.1
9492  * Copyright(c) 2006-2007, Ext JS, LLC.
9493  *
9494  * Originally Released Under LGPL - original licence link has changed is not relivant.
9495  *
9496  * Fork - LGPL
9497  * <script type="text/javascript">
9498  */
9499
9500 /**
9501 * @class Roo.data.Record
9502  * Instances of this class encapsulate both record <em>definition</em> information, and record
9503  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9504  * to access Records cached in an {@link Roo.data.Store} object.<br>
9505  * <p>
9506  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9507  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9508  * objects.<br>
9509  * <p>
9510  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9511  * @constructor
9512  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9513  * {@link #create}. The parameters are the same.
9514  * @param {Array} data An associative Array of data values keyed by the field name.
9515  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9516  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9517  * not specified an integer id is generated.
9518  */
9519 Roo.data.Record = function(data, id){
9520     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9521     this.data = data;
9522 };
9523
9524 /**
9525  * Generate a constructor for a specific record layout.
9526  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9527  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9528  * Each field definition object may contain the following properties: <ul>
9529  * <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,
9530  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9531  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9532  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9533  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9534  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9535  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9536  * this may be omitted.</p></li>
9537  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9538  * <ul><li>auto (Default, implies no conversion)</li>
9539  * <li>string</li>
9540  * <li>int</li>
9541  * <li>float</li>
9542  * <li>boolean</li>
9543  * <li>date</li></ul></p></li>
9544  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9545  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9546  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9547  * by the Reader into an object that will be stored in the Record. It is passed the
9548  * following parameters:<ul>
9549  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9550  * </ul></p></li>
9551  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9552  * </ul>
9553  * <br>usage:<br><pre><code>
9554 var TopicRecord = Roo.data.Record.create(
9555     {name: 'title', mapping: 'topic_title'},
9556     {name: 'author', mapping: 'username'},
9557     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9558     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9559     {name: 'lastPoster', mapping: 'user2'},
9560     {name: 'excerpt', mapping: 'post_text'}
9561 );
9562
9563 var myNewRecord = new TopicRecord({
9564     title: 'Do my job please',
9565     author: 'noobie',
9566     totalPosts: 1,
9567     lastPost: new Date(),
9568     lastPoster: 'Animal',
9569     excerpt: 'No way dude!'
9570 });
9571 myStore.add(myNewRecord);
9572 </code></pre>
9573  * @method create
9574  * @static
9575  */
9576 Roo.data.Record.create = function(o){
9577     var f = function(){
9578         f.superclass.constructor.apply(this, arguments);
9579     };
9580     Roo.extend(f, Roo.data.Record);
9581     var p = f.prototype;
9582     p.fields = new Roo.util.MixedCollection(false, function(field){
9583         return field.name;
9584     });
9585     for(var i = 0, len = o.length; i < len; i++){
9586         p.fields.add(new Roo.data.Field(o[i]));
9587     }
9588     f.getField = function(name){
9589         return p.fields.get(name);  
9590     };
9591     return f;
9592 };
9593
9594 Roo.data.Record.AUTO_ID = 1000;
9595 Roo.data.Record.EDIT = 'edit';
9596 Roo.data.Record.REJECT = 'reject';
9597 Roo.data.Record.COMMIT = 'commit';
9598
9599 Roo.data.Record.prototype = {
9600     /**
9601      * Readonly flag - true if this record has been modified.
9602      * @type Boolean
9603      */
9604     dirty : false,
9605     editing : false,
9606     error: null,
9607     modified: null,
9608
9609     // private
9610     join : function(store){
9611         this.store = store;
9612     },
9613
9614     /**
9615      * Set the named field to the specified value.
9616      * @param {String} name The name of the field to set.
9617      * @param {Object} value The value to set the field to.
9618      */
9619     set : function(name, value){
9620         if(this.data[name] == value){
9621             return;
9622         }
9623         this.dirty = true;
9624         if(!this.modified){
9625             this.modified = {};
9626         }
9627         if(typeof this.modified[name] == 'undefined'){
9628             this.modified[name] = this.data[name];
9629         }
9630         this.data[name] = value;
9631         if(!this.editing && this.store){
9632             this.store.afterEdit(this);
9633         }       
9634     },
9635
9636     /**
9637      * Get the value of the named field.
9638      * @param {String} name The name of the field to get the value of.
9639      * @return {Object} The value of the field.
9640      */
9641     get : function(name){
9642         return this.data[name]; 
9643     },
9644
9645     // private
9646     beginEdit : function(){
9647         this.editing = true;
9648         this.modified = {}; 
9649     },
9650
9651     // private
9652     cancelEdit : function(){
9653         this.editing = false;
9654         delete this.modified;
9655     },
9656
9657     // private
9658     endEdit : function(){
9659         this.editing = false;
9660         if(this.dirty && this.store){
9661             this.store.afterEdit(this);
9662         }
9663     },
9664
9665     /**
9666      * Usually called by the {@link Roo.data.Store} which owns the Record.
9667      * Rejects all changes made to the Record since either creation, or the last commit operation.
9668      * Modified fields are reverted to their original values.
9669      * <p>
9670      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9671      * of reject operations.
9672      */
9673     reject : function(){
9674         var m = this.modified;
9675         for(var n in m){
9676             if(typeof m[n] != "function"){
9677                 this.data[n] = m[n];
9678             }
9679         }
9680         this.dirty = false;
9681         delete this.modified;
9682         this.editing = false;
9683         if(this.store){
9684             this.store.afterReject(this);
9685         }
9686     },
9687
9688     /**
9689      * Usually called by the {@link Roo.data.Store} which owns the Record.
9690      * Commits all changes made to the Record since either creation, or the last commit operation.
9691      * <p>
9692      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9693      * of commit operations.
9694      */
9695     commit : function(){
9696         this.dirty = false;
9697         delete this.modified;
9698         this.editing = false;
9699         if(this.store){
9700             this.store.afterCommit(this);
9701         }
9702     },
9703
9704     // private
9705     hasError : function(){
9706         return this.error != null;
9707     },
9708
9709     // private
9710     clearError : function(){
9711         this.error = null;
9712     },
9713
9714     /**
9715      * Creates a copy of this record.
9716      * @param {String} id (optional) A new record id if you don't want to use this record's id
9717      * @return {Record}
9718      */
9719     copy : function(newId) {
9720         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9721     }
9722 };/*
9723  * Based on:
9724  * Ext JS Library 1.1.1
9725  * Copyright(c) 2006-2007, Ext JS, LLC.
9726  *
9727  * Originally Released Under LGPL - original licence link has changed is not relivant.
9728  *
9729  * Fork - LGPL
9730  * <script type="text/javascript">
9731  */
9732
9733
9734
9735 /**
9736  * @class Roo.data.Store
9737  * @extends Roo.util.Observable
9738  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9739  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9740  * <p>
9741  * 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
9742  * has no knowledge of the format of the data returned by the Proxy.<br>
9743  * <p>
9744  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9745  * instances from the data object. These records are cached and made available through accessor functions.
9746  * @constructor
9747  * Creates a new Store.
9748  * @param {Object} config A config object containing the objects needed for the Store to access data,
9749  * and read the data into Records.
9750  */
9751 Roo.data.Store = function(config){
9752     this.data = new Roo.util.MixedCollection(false);
9753     this.data.getKey = function(o){
9754         return o.id;
9755     };
9756     this.baseParams = {};
9757     // private
9758     this.paramNames = {
9759         "start" : "start",
9760         "limit" : "limit",
9761         "sort" : "sort",
9762         "dir" : "dir",
9763         "multisort" : "_multisort"
9764     };
9765
9766     if(config && config.data){
9767         this.inlineData = config.data;
9768         delete config.data;
9769     }
9770
9771     Roo.apply(this, config);
9772     
9773     if(this.reader){ // reader passed
9774         this.reader = Roo.factory(this.reader, Roo.data);
9775         this.reader.xmodule = this.xmodule || false;
9776         if(!this.recordType){
9777             this.recordType = this.reader.recordType;
9778         }
9779         if(this.reader.onMetaChange){
9780             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9781         }
9782     }
9783
9784     if(this.recordType){
9785         this.fields = this.recordType.prototype.fields;
9786     }
9787     this.modified = [];
9788
9789     this.addEvents({
9790         /**
9791          * @event datachanged
9792          * Fires when the data cache has changed, and a widget which is using this Store
9793          * as a Record cache should refresh its view.
9794          * @param {Store} this
9795          */
9796         datachanged : true,
9797         /**
9798          * @event metachange
9799          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9800          * @param {Store} this
9801          * @param {Object} meta The JSON metadata
9802          */
9803         metachange : true,
9804         /**
9805          * @event add
9806          * Fires when Records have been added to the Store
9807          * @param {Store} this
9808          * @param {Roo.data.Record[]} records The array of Records added
9809          * @param {Number} index The index at which the record(s) were added
9810          */
9811         add : true,
9812         /**
9813          * @event remove
9814          * Fires when a Record has been removed from the Store
9815          * @param {Store} this
9816          * @param {Roo.data.Record} record The Record that was removed
9817          * @param {Number} index The index at which the record was removed
9818          */
9819         remove : true,
9820         /**
9821          * @event update
9822          * Fires when a Record has been updated
9823          * @param {Store} this
9824          * @param {Roo.data.Record} record The Record that was updated
9825          * @param {String} operation The update operation being performed.  Value may be one of:
9826          * <pre><code>
9827  Roo.data.Record.EDIT
9828  Roo.data.Record.REJECT
9829  Roo.data.Record.COMMIT
9830          * </code></pre>
9831          */
9832         update : true,
9833         /**
9834          * @event clear
9835          * Fires when the data cache has been cleared.
9836          * @param {Store} this
9837          */
9838         clear : true,
9839         /**
9840          * @event beforeload
9841          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9842          * the load action will be canceled.
9843          * @param {Store} this
9844          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9845          */
9846         beforeload : true,
9847         /**
9848          * @event beforeloadadd
9849          * Fires after a new set of Records has been loaded.
9850          * @param {Store} this
9851          * @param {Roo.data.Record[]} records The Records that were loaded
9852          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9853          */
9854         beforeloadadd : true,
9855         /**
9856          * @event load
9857          * Fires after a new set of Records has been loaded, before they are added to the store.
9858          * @param {Store} this
9859          * @param {Roo.data.Record[]} records The Records that were loaded
9860          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9861          * @params {Object} return from reader
9862          */
9863         load : true,
9864         /**
9865          * @event loadexception
9866          * Fires if an exception occurs in the Proxy during loading.
9867          * Called with the signature of the Proxy's "loadexception" event.
9868          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9869          * 
9870          * @param {Proxy} 
9871          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9872          * @param {Object} load options 
9873          * @param {Object} jsonData from your request (normally this contains the Exception)
9874          */
9875         loadexception : true
9876     });
9877     
9878     if(this.proxy){
9879         this.proxy = Roo.factory(this.proxy, Roo.data);
9880         this.proxy.xmodule = this.xmodule || false;
9881         this.relayEvents(this.proxy,  ["loadexception"]);
9882     }
9883     this.sortToggle = {};
9884     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9885
9886     Roo.data.Store.superclass.constructor.call(this);
9887
9888     if(this.inlineData){
9889         this.loadData(this.inlineData);
9890         delete this.inlineData;
9891     }
9892 };
9893
9894 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9895      /**
9896     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9897     * without a remote query - used by combo/forms at present.
9898     */
9899     
9900     /**
9901     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9902     */
9903     /**
9904     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9905     */
9906     /**
9907     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9908     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9909     */
9910     /**
9911     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9912     * on any HTTP request
9913     */
9914     /**
9915     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9916     */
9917     /**
9918     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9919     */
9920     multiSort: false,
9921     /**
9922     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9923     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9924     */
9925     remoteSort : false,
9926
9927     /**
9928     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9929      * loaded or when a record is removed. (defaults to false).
9930     */
9931     pruneModifiedRecords : false,
9932
9933     // private
9934     lastOptions : null,
9935
9936     /**
9937      * Add Records to the Store and fires the add event.
9938      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9939      */
9940     add : function(records){
9941         records = [].concat(records);
9942         for(var i = 0, len = records.length; i < len; i++){
9943             records[i].join(this);
9944         }
9945         var index = this.data.length;
9946         this.data.addAll(records);
9947         this.fireEvent("add", this, records, index);
9948     },
9949
9950     /**
9951      * Remove a Record from the Store and fires the remove event.
9952      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9953      */
9954     remove : function(record){
9955         var index = this.data.indexOf(record);
9956         this.data.removeAt(index);
9957         if(this.pruneModifiedRecords){
9958             this.modified.remove(record);
9959         }
9960         this.fireEvent("remove", this, record, index);
9961     },
9962
9963     /**
9964      * Remove all Records from the Store and fires the clear event.
9965      */
9966     removeAll : function(){
9967         this.data.clear();
9968         if(this.pruneModifiedRecords){
9969             this.modified = [];
9970         }
9971         this.fireEvent("clear", this);
9972     },
9973
9974     /**
9975      * Inserts Records to the Store at the given index and fires the add event.
9976      * @param {Number} index The start index at which to insert the passed Records.
9977      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9978      */
9979     insert : function(index, records){
9980         records = [].concat(records);
9981         for(var i = 0, len = records.length; i < len; i++){
9982             this.data.insert(index, records[i]);
9983             records[i].join(this);
9984         }
9985         this.fireEvent("add", this, records, index);
9986     },
9987
9988     /**
9989      * Get the index within the cache of the passed Record.
9990      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9991      * @return {Number} The index of the passed Record. Returns -1 if not found.
9992      */
9993     indexOf : function(record){
9994         return this.data.indexOf(record);
9995     },
9996
9997     /**
9998      * Get the index within the cache of the Record with the passed id.
9999      * @param {String} id The id of the Record to find.
10000      * @return {Number} The index of the Record. Returns -1 if not found.
10001      */
10002     indexOfId : function(id){
10003         return this.data.indexOfKey(id);
10004     },
10005
10006     /**
10007      * Get the Record with the specified id.
10008      * @param {String} id The id of the Record to find.
10009      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10010      */
10011     getById : function(id){
10012         return this.data.key(id);
10013     },
10014
10015     /**
10016      * Get the Record at the specified index.
10017      * @param {Number} index The index of the Record to find.
10018      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10019      */
10020     getAt : function(index){
10021         return this.data.itemAt(index);
10022     },
10023
10024     /**
10025      * Returns a range of Records between specified indices.
10026      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10027      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10028      * @return {Roo.data.Record[]} An array of Records
10029      */
10030     getRange : function(start, end){
10031         return this.data.getRange(start, end);
10032     },
10033
10034     // private
10035     storeOptions : function(o){
10036         o = Roo.apply({}, o);
10037         delete o.callback;
10038         delete o.scope;
10039         this.lastOptions = o;
10040     },
10041
10042     /**
10043      * Loads the Record cache from the configured Proxy using the configured Reader.
10044      * <p>
10045      * If using remote paging, then the first load call must specify the <em>start</em>
10046      * and <em>limit</em> properties in the options.params property to establish the initial
10047      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10048      * <p>
10049      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10050      * and this call will return before the new data has been loaded. Perform any post-processing
10051      * in a callback function, or in a "load" event handler.</strong>
10052      * <p>
10053      * @param {Object} options An object containing properties which control loading options:<ul>
10054      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10055      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10056      * passed the following arguments:<ul>
10057      * <li>r : Roo.data.Record[]</li>
10058      * <li>options: Options object from the load call</li>
10059      * <li>success: Boolean success indicator</li></ul></li>
10060      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10061      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10062      * </ul>
10063      */
10064     load : function(options){
10065         options = options || {};
10066         if(this.fireEvent("beforeload", this, options) !== false){
10067             this.storeOptions(options);
10068             var p = Roo.apply(options.params || {}, this.baseParams);
10069             // if meta was not loaded from remote source.. try requesting it.
10070             if (!this.reader.metaFromRemote) {
10071                 p._requestMeta = 1;
10072             }
10073             if(this.sortInfo && this.remoteSort){
10074                 var pn = this.paramNames;
10075                 p[pn["sort"]] = this.sortInfo.field;
10076                 p[pn["dir"]] = this.sortInfo.direction;
10077             }
10078             if (this.multiSort) {
10079                 var pn = this.paramNames;
10080                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10081             }
10082             
10083             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10084         }
10085     },
10086
10087     /**
10088      * Reloads the Record cache from the configured Proxy using the configured Reader and
10089      * the options from the last load operation performed.
10090      * @param {Object} options (optional) An object containing properties which may override the options
10091      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10092      * the most recently used options are reused).
10093      */
10094     reload : function(options){
10095         this.load(Roo.applyIf(options||{}, this.lastOptions));
10096     },
10097
10098     // private
10099     // Called as a callback by the Reader during a load operation.
10100     loadRecords : function(o, options, success){
10101         if(!o || success === false){
10102             if(success !== false){
10103                 this.fireEvent("load", this, [], options, o);
10104             }
10105             if(options.callback){
10106                 options.callback.call(options.scope || this, [], options, false);
10107             }
10108             return;
10109         }
10110         // if data returned failure - throw an exception.
10111         if (o.success === false) {
10112             // show a message if no listener is registered.
10113             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10114                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10115             }
10116             // loadmask wil be hooked into this..
10117             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10118             return;
10119         }
10120         var r = o.records, t = o.totalRecords || r.length;
10121         
10122         this.fireEvent("beforeloadadd", this, r, options, o);
10123         
10124         if(!options || options.add !== true){
10125             if(this.pruneModifiedRecords){
10126                 this.modified = [];
10127             }
10128             for(var i = 0, len = r.length; i < len; i++){
10129                 r[i].join(this);
10130             }
10131             if(this.snapshot){
10132                 this.data = this.snapshot;
10133                 delete this.snapshot;
10134             }
10135             this.data.clear();
10136             this.data.addAll(r);
10137             this.totalLength = t;
10138             this.applySort();
10139             this.fireEvent("datachanged", this);
10140         }else{
10141             this.totalLength = Math.max(t, this.data.length+r.length);
10142             this.add(r);
10143         }
10144         this.fireEvent("load", this, r, options, o);
10145         if(options.callback){
10146             options.callback.call(options.scope || this, r, options, true);
10147         }
10148     },
10149
10150
10151     /**
10152      * Loads data from a passed data block. A Reader which understands the format of the data
10153      * must have been configured in the constructor.
10154      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10155      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10156      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10157      */
10158     loadData : function(o, append){
10159         var r = this.reader.readRecords(o);
10160         this.loadRecords(r, {add: append}, true);
10161     },
10162
10163     /**
10164      * Gets the number of cached records.
10165      * <p>
10166      * <em>If using paging, this may not be the total size of the dataset. If the data object
10167      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10168      * the data set size</em>
10169      */
10170     getCount : function(){
10171         return this.data.length || 0;
10172     },
10173
10174     /**
10175      * Gets the total number of records in the dataset as returned by the server.
10176      * <p>
10177      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10178      * the dataset size</em>
10179      */
10180     getTotalCount : function(){
10181         return this.totalLength || 0;
10182     },
10183
10184     /**
10185      * Returns the sort state of the Store as an object with two properties:
10186      * <pre><code>
10187  field {String} The name of the field by which the Records are sorted
10188  direction {String} The sort order, "ASC" or "DESC"
10189      * </code></pre>
10190      */
10191     getSortState : function(){
10192         return this.sortInfo;
10193     },
10194
10195     // private
10196     applySort : function(){
10197         if(this.sortInfo && !this.remoteSort){
10198             var s = this.sortInfo, f = s.field;
10199             var st = this.fields.get(f).sortType;
10200             var fn = function(r1, r2){
10201                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10202                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10203             };
10204             this.data.sort(s.direction, fn);
10205             if(this.snapshot && this.snapshot != this.data){
10206                 this.snapshot.sort(s.direction, fn);
10207             }
10208         }
10209     },
10210
10211     /**
10212      * Sets the default sort column and order to be used by the next load operation.
10213      * @param {String} fieldName The name of the field to sort by.
10214      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10215      */
10216     setDefaultSort : function(field, dir){
10217         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10218     },
10219
10220     /**
10221      * Sort the Records.
10222      * If remote sorting is used, the sort is performed on the server, and the cache is
10223      * reloaded. If local sorting is used, the cache is sorted internally.
10224      * @param {String} fieldName The name of the field to sort by.
10225      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10226      */
10227     sort : function(fieldName, dir){
10228         var f = this.fields.get(fieldName);
10229         if(!dir){
10230             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10231             
10232             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10233                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10234             }else{
10235                 dir = f.sortDir;
10236             }
10237         }
10238         this.sortToggle[f.name] = dir;
10239         this.sortInfo = {field: f.name, direction: dir};
10240         if(!this.remoteSort){
10241             this.applySort();
10242             this.fireEvent("datachanged", this);
10243         }else{
10244             this.load(this.lastOptions);
10245         }
10246     },
10247
10248     /**
10249      * Calls the specified function for each of the Records in the cache.
10250      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10251      * Returning <em>false</em> aborts and exits the iteration.
10252      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10253      */
10254     each : function(fn, scope){
10255         this.data.each(fn, scope);
10256     },
10257
10258     /**
10259      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10260      * (e.g., during paging).
10261      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10262      */
10263     getModifiedRecords : function(){
10264         return this.modified;
10265     },
10266
10267     // private
10268     createFilterFn : function(property, value, anyMatch){
10269         if(!value.exec){ // not a regex
10270             value = String(value);
10271             if(value.length == 0){
10272                 return false;
10273             }
10274             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10275         }
10276         return function(r){
10277             return value.test(r.data[property]);
10278         };
10279     },
10280
10281     /**
10282      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10283      * @param {String} property A field on your records
10284      * @param {Number} start The record index to start at (defaults to 0)
10285      * @param {Number} end The last record index to include (defaults to length - 1)
10286      * @return {Number} The sum
10287      */
10288     sum : function(property, start, end){
10289         var rs = this.data.items, v = 0;
10290         start = start || 0;
10291         end = (end || end === 0) ? end : rs.length-1;
10292
10293         for(var i = start; i <= end; i++){
10294             v += (rs[i].data[property] || 0);
10295         }
10296         return v;
10297     },
10298
10299     /**
10300      * Filter the records by a specified property.
10301      * @param {String} field A field on your records
10302      * @param {String/RegExp} value Either a string that the field
10303      * should start with or a RegExp to test against the field
10304      * @param {Boolean} anyMatch True to match any part not just the beginning
10305      */
10306     filter : function(property, value, anyMatch){
10307         var fn = this.createFilterFn(property, value, anyMatch);
10308         return fn ? this.filterBy(fn) : this.clearFilter();
10309     },
10310
10311     /**
10312      * Filter by a function. The specified function will be called with each
10313      * record in this data source. If the function returns true the record is included,
10314      * otherwise it is filtered.
10315      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10316      * @param {Object} scope (optional) The scope of the function (defaults to this)
10317      */
10318     filterBy : function(fn, scope){
10319         this.snapshot = this.snapshot || this.data;
10320         this.data = this.queryBy(fn, scope||this);
10321         this.fireEvent("datachanged", this);
10322     },
10323
10324     /**
10325      * Query the records by a specified property.
10326      * @param {String} field A field on your records
10327      * @param {String/RegExp} value Either a string that the field
10328      * should start with or a RegExp to test against the field
10329      * @param {Boolean} anyMatch True to match any part not just the beginning
10330      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10331      */
10332     query : function(property, value, anyMatch){
10333         var fn = this.createFilterFn(property, value, anyMatch);
10334         return fn ? this.queryBy(fn) : this.data.clone();
10335     },
10336
10337     /**
10338      * Query by a function. The specified function will be called with each
10339      * record in this data source. If the function returns true the record is included
10340      * in the results.
10341      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10342      * @param {Object} scope (optional) The scope of the function (defaults to this)
10343       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10344      **/
10345     queryBy : function(fn, scope){
10346         var data = this.snapshot || this.data;
10347         return data.filterBy(fn, scope||this);
10348     },
10349
10350     /**
10351      * Collects unique values for a particular dataIndex from this store.
10352      * @param {String} dataIndex The property to collect
10353      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10354      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10355      * @return {Array} An array of the unique values
10356      **/
10357     collect : function(dataIndex, allowNull, bypassFilter){
10358         var d = (bypassFilter === true && this.snapshot) ?
10359                 this.snapshot.items : this.data.items;
10360         var v, sv, r = [], l = {};
10361         for(var i = 0, len = d.length; i < len; i++){
10362             v = d[i].data[dataIndex];
10363             sv = String(v);
10364             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10365                 l[sv] = true;
10366                 r[r.length] = v;
10367             }
10368         }
10369         return r;
10370     },
10371
10372     /**
10373      * Revert to a view of the Record cache with no filtering applied.
10374      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10375      */
10376     clearFilter : function(suppressEvent){
10377         if(this.snapshot && this.snapshot != this.data){
10378             this.data = this.snapshot;
10379             delete this.snapshot;
10380             if(suppressEvent !== true){
10381                 this.fireEvent("datachanged", this);
10382             }
10383         }
10384     },
10385
10386     // private
10387     afterEdit : function(record){
10388         if(this.modified.indexOf(record) == -1){
10389             this.modified.push(record);
10390         }
10391         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10392     },
10393     
10394     // private
10395     afterReject : function(record){
10396         this.modified.remove(record);
10397         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10398     },
10399
10400     // private
10401     afterCommit : function(record){
10402         this.modified.remove(record);
10403         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10404     },
10405
10406     /**
10407      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10408      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10409      */
10410     commitChanges : function(){
10411         var m = this.modified.slice(0);
10412         this.modified = [];
10413         for(var i = 0, len = m.length; i < len; i++){
10414             m[i].commit();
10415         }
10416     },
10417
10418     /**
10419      * Cancel outstanding changes on all changed records.
10420      */
10421     rejectChanges : function(){
10422         var m = this.modified.slice(0);
10423         this.modified = [];
10424         for(var i = 0, len = m.length; i < len; i++){
10425             m[i].reject();
10426         }
10427     },
10428
10429     onMetaChange : function(meta, rtype, o){
10430         this.recordType = rtype;
10431         this.fields = rtype.prototype.fields;
10432         delete this.snapshot;
10433         this.sortInfo = meta.sortInfo || this.sortInfo;
10434         this.modified = [];
10435         this.fireEvent('metachange', this, this.reader.meta);
10436     },
10437     
10438     moveIndex : function(data, type)
10439     {
10440         var index = this.indexOf(data);
10441         
10442         var newIndex = index + type;
10443         
10444         this.remove(data);
10445         
10446         this.insert(newIndex, data);
10447         
10448     }
10449 });/*
10450  * Based on:
10451  * Ext JS Library 1.1.1
10452  * Copyright(c) 2006-2007, Ext JS, LLC.
10453  *
10454  * Originally Released Under LGPL - original licence link has changed is not relivant.
10455  *
10456  * Fork - LGPL
10457  * <script type="text/javascript">
10458  */
10459
10460 /**
10461  * @class Roo.data.SimpleStore
10462  * @extends Roo.data.Store
10463  * Small helper class to make creating Stores from Array data easier.
10464  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10465  * @cfg {Array} fields An array of field definition objects, or field name strings.
10466  * @cfg {Array} data The multi-dimensional array of data
10467  * @constructor
10468  * @param {Object} config
10469  */
10470 Roo.data.SimpleStore = function(config){
10471     Roo.data.SimpleStore.superclass.constructor.call(this, {
10472         isLocal : true,
10473         reader: new Roo.data.ArrayReader({
10474                 id: config.id
10475             },
10476             Roo.data.Record.create(config.fields)
10477         ),
10478         proxy : new Roo.data.MemoryProxy(config.data)
10479     });
10480     this.load();
10481 };
10482 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10483  * Based on:
10484  * Ext JS Library 1.1.1
10485  * Copyright(c) 2006-2007, Ext JS, LLC.
10486  *
10487  * Originally Released Under LGPL - original licence link has changed is not relivant.
10488  *
10489  * Fork - LGPL
10490  * <script type="text/javascript">
10491  */
10492
10493 /**
10494 /**
10495  * @extends Roo.data.Store
10496  * @class Roo.data.JsonStore
10497  * Small helper class to make creating Stores for JSON data easier. <br/>
10498 <pre><code>
10499 var store = new Roo.data.JsonStore({
10500     url: 'get-images.php',
10501     root: 'images',
10502     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10503 });
10504 </code></pre>
10505  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10506  * JsonReader and HttpProxy (unless inline data is provided).</b>
10507  * @cfg {Array} fields An array of field definition objects, or field name strings.
10508  * @constructor
10509  * @param {Object} config
10510  */
10511 Roo.data.JsonStore = function(c){
10512     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10513         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10514         reader: new Roo.data.JsonReader(c, c.fields)
10515     }));
10516 };
10517 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10518  * Based on:
10519  * Ext JS Library 1.1.1
10520  * Copyright(c) 2006-2007, Ext JS, LLC.
10521  *
10522  * Originally Released Under LGPL - original licence link has changed is not relivant.
10523  *
10524  * Fork - LGPL
10525  * <script type="text/javascript">
10526  */
10527
10528  
10529 Roo.data.Field = function(config){
10530     if(typeof config == "string"){
10531         config = {name: config};
10532     }
10533     Roo.apply(this, config);
10534     
10535     if(!this.type){
10536         this.type = "auto";
10537     }
10538     
10539     var st = Roo.data.SortTypes;
10540     // named sortTypes are supported, here we look them up
10541     if(typeof this.sortType == "string"){
10542         this.sortType = st[this.sortType];
10543     }
10544     
10545     // set default sortType for strings and dates
10546     if(!this.sortType){
10547         switch(this.type){
10548             case "string":
10549                 this.sortType = st.asUCString;
10550                 break;
10551             case "date":
10552                 this.sortType = st.asDate;
10553                 break;
10554             default:
10555                 this.sortType = st.none;
10556         }
10557     }
10558
10559     // define once
10560     var stripRe = /[\$,%]/g;
10561
10562     // prebuilt conversion function for this field, instead of
10563     // switching every time we're reading a value
10564     if(!this.convert){
10565         var cv, dateFormat = this.dateFormat;
10566         switch(this.type){
10567             case "":
10568             case "auto":
10569             case undefined:
10570                 cv = function(v){ return v; };
10571                 break;
10572             case "string":
10573                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10574                 break;
10575             case "int":
10576                 cv = function(v){
10577                     return v !== undefined && v !== null && v !== '' ?
10578                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10579                     };
10580                 break;
10581             case "float":
10582                 cv = function(v){
10583                     return v !== undefined && v !== null && v !== '' ?
10584                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10585                     };
10586                 break;
10587             case "bool":
10588             case "boolean":
10589                 cv = function(v){ return v === true || v === "true" || v == 1; };
10590                 break;
10591             case "date":
10592                 cv = function(v){
10593                     if(!v){
10594                         return '';
10595                     }
10596                     if(v instanceof Date){
10597                         return v;
10598                     }
10599                     if(dateFormat){
10600                         if(dateFormat == "timestamp"){
10601                             return new Date(v*1000);
10602                         }
10603                         return Date.parseDate(v, dateFormat);
10604                     }
10605                     var parsed = Date.parse(v);
10606                     return parsed ? new Date(parsed) : null;
10607                 };
10608              break;
10609             
10610         }
10611         this.convert = cv;
10612     }
10613 };
10614
10615 Roo.data.Field.prototype = {
10616     dateFormat: null,
10617     defaultValue: "",
10618     mapping: null,
10619     sortType : null,
10620     sortDir : "ASC"
10621 };/*
10622  * Based on:
10623  * Ext JS Library 1.1.1
10624  * Copyright(c) 2006-2007, Ext JS, LLC.
10625  *
10626  * Originally Released Under LGPL - original licence link has changed is not relivant.
10627  *
10628  * Fork - LGPL
10629  * <script type="text/javascript">
10630  */
10631  
10632 // Base class for reading structured data from a data source.  This class is intended to be
10633 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10634
10635 /**
10636  * @class Roo.data.DataReader
10637  * Base class for reading structured data from a data source.  This class is intended to be
10638  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10639  */
10640
10641 Roo.data.DataReader = function(meta, recordType){
10642     
10643     this.meta = meta;
10644     
10645     this.recordType = recordType instanceof Array ? 
10646         Roo.data.Record.create(recordType) : recordType;
10647 };
10648
10649 Roo.data.DataReader.prototype = {
10650      /**
10651      * Create an empty record
10652      * @param {Object} data (optional) - overlay some values
10653      * @return {Roo.data.Record} record created.
10654      */
10655     newRow :  function(d) {
10656         var da =  {};
10657         this.recordType.prototype.fields.each(function(c) {
10658             switch( c.type) {
10659                 case 'int' : da[c.name] = 0; break;
10660                 case 'date' : da[c.name] = new Date(); break;
10661                 case 'float' : da[c.name] = 0.0; break;
10662                 case 'boolean' : da[c.name] = false; break;
10663                 default : da[c.name] = ""; break;
10664             }
10665             
10666         });
10667         return new this.recordType(Roo.apply(da, d));
10668     }
10669     
10670 };/*
10671  * Based on:
10672  * Ext JS Library 1.1.1
10673  * Copyright(c) 2006-2007, Ext JS, LLC.
10674  *
10675  * Originally Released Under LGPL - original licence link has changed is not relivant.
10676  *
10677  * Fork - LGPL
10678  * <script type="text/javascript">
10679  */
10680
10681 /**
10682  * @class Roo.data.DataProxy
10683  * @extends Roo.data.Observable
10684  * This class is an abstract base class for implementations which provide retrieval of
10685  * unformatted data objects.<br>
10686  * <p>
10687  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10688  * (of the appropriate type which knows how to parse the data object) to provide a block of
10689  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10690  * <p>
10691  * Custom implementations must implement the load method as described in
10692  * {@link Roo.data.HttpProxy#load}.
10693  */
10694 Roo.data.DataProxy = function(){
10695     this.addEvents({
10696         /**
10697          * @event beforeload
10698          * Fires before a network request is made to retrieve a data object.
10699          * @param {Object} This DataProxy object.
10700          * @param {Object} params The params parameter to the load function.
10701          */
10702         beforeload : true,
10703         /**
10704          * @event load
10705          * Fires before the load method's callback is called.
10706          * @param {Object} This DataProxy object.
10707          * @param {Object} o The data object.
10708          * @param {Object} arg The callback argument object passed to the load function.
10709          */
10710         load : true,
10711         /**
10712          * @event loadexception
10713          * Fires if an Exception occurs during data retrieval.
10714          * @param {Object} This DataProxy object.
10715          * @param {Object} o The data object.
10716          * @param {Object} arg The callback argument object passed to the load function.
10717          * @param {Object} e The Exception.
10718          */
10719         loadexception : true
10720     });
10721     Roo.data.DataProxy.superclass.constructor.call(this);
10722 };
10723
10724 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10725
10726     /**
10727      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10728      */
10729 /*
10730  * Based on:
10731  * Ext JS Library 1.1.1
10732  * Copyright(c) 2006-2007, Ext JS, LLC.
10733  *
10734  * Originally Released Under LGPL - original licence link has changed is not relivant.
10735  *
10736  * Fork - LGPL
10737  * <script type="text/javascript">
10738  */
10739 /**
10740  * @class Roo.data.MemoryProxy
10741  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10742  * to the Reader when its load method is called.
10743  * @constructor
10744  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10745  */
10746 Roo.data.MemoryProxy = function(data){
10747     if (data.data) {
10748         data = data.data;
10749     }
10750     Roo.data.MemoryProxy.superclass.constructor.call(this);
10751     this.data = data;
10752 };
10753
10754 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10755     /**
10756      * Load data from the requested source (in this case an in-memory
10757      * data object passed to the constructor), read the data object into
10758      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10759      * process that block using the passed callback.
10760      * @param {Object} params This parameter is not used by the MemoryProxy class.
10761      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10762      * object into a block of Roo.data.Records.
10763      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10764      * The function must be passed <ul>
10765      * <li>The Record block object</li>
10766      * <li>The "arg" argument from the load function</li>
10767      * <li>A boolean success indicator</li>
10768      * </ul>
10769      * @param {Object} scope The scope in which to call the callback
10770      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10771      */
10772     load : function(params, reader, callback, scope, arg){
10773         params = params || {};
10774         var result;
10775         try {
10776             result = reader.readRecords(this.data);
10777         }catch(e){
10778             this.fireEvent("loadexception", this, arg, null, e);
10779             callback.call(scope, null, arg, false);
10780             return;
10781         }
10782         callback.call(scope, result, arg, true);
10783     },
10784     
10785     // private
10786     update : function(params, records){
10787         
10788     }
10789 });/*
10790  * Based on:
10791  * Ext JS Library 1.1.1
10792  * Copyright(c) 2006-2007, Ext JS, LLC.
10793  *
10794  * Originally Released Under LGPL - original licence link has changed is not relivant.
10795  *
10796  * Fork - LGPL
10797  * <script type="text/javascript">
10798  */
10799 /**
10800  * @class Roo.data.HttpProxy
10801  * @extends Roo.data.DataProxy
10802  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10803  * configured to reference a certain URL.<br><br>
10804  * <p>
10805  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10806  * from which the running page was served.<br><br>
10807  * <p>
10808  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10809  * <p>
10810  * Be aware that to enable the browser to parse an XML document, the server must set
10811  * the Content-Type header in the HTTP response to "text/xml".
10812  * @constructor
10813  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10814  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10815  * will be used to make the request.
10816  */
10817 Roo.data.HttpProxy = function(conn){
10818     Roo.data.HttpProxy.superclass.constructor.call(this);
10819     // is conn a conn config or a real conn?
10820     this.conn = conn;
10821     this.useAjax = !conn || !conn.events;
10822   
10823 };
10824
10825 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10826     // thse are take from connection...
10827     
10828     /**
10829      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10830      */
10831     /**
10832      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10833      * extra parameters to each request made by this object. (defaults to undefined)
10834      */
10835     /**
10836      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10837      *  to each request made by this object. (defaults to undefined)
10838      */
10839     /**
10840      * @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)
10841      */
10842     /**
10843      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10844      */
10845      /**
10846      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10847      * @type Boolean
10848      */
10849   
10850
10851     /**
10852      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10853      * @type Boolean
10854      */
10855     /**
10856      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10857      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10858      * a finer-grained basis than the DataProxy events.
10859      */
10860     getConnection : function(){
10861         return this.useAjax ? Roo.Ajax : this.conn;
10862     },
10863
10864     /**
10865      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10866      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10867      * process that block using the passed callback.
10868      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10869      * for the request to the remote server.
10870      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10871      * object into a block of Roo.data.Records.
10872      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10873      * The function must be passed <ul>
10874      * <li>The Record block object</li>
10875      * <li>The "arg" argument from the load function</li>
10876      * <li>A boolean success indicator</li>
10877      * </ul>
10878      * @param {Object} scope The scope in which to call the callback
10879      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10880      */
10881     load : function(params, reader, callback, scope, arg){
10882         if(this.fireEvent("beforeload", this, params) !== false){
10883             var  o = {
10884                 params : params || {},
10885                 request: {
10886                     callback : callback,
10887                     scope : scope,
10888                     arg : arg
10889                 },
10890                 reader: reader,
10891                 callback : this.loadResponse,
10892                 scope: this
10893             };
10894             if(this.useAjax){
10895                 Roo.applyIf(o, this.conn);
10896                 if(this.activeRequest){
10897                     Roo.Ajax.abort(this.activeRequest);
10898                 }
10899                 this.activeRequest = Roo.Ajax.request(o);
10900             }else{
10901                 this.conn.request(o);
10902             }
10903         }else{
10904             callback.call(scope||this, null, arg, false);
10905         }
10906     },
10907
10908     // private
10909     loadResponse : function(o, success, response){
10910         delete this.activeRequest;
10911         if(!success){
10912             this.fireEvent("loadexception", this, o, response);
10913             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10914             return;
10915         }
10916         var result;
10917         try {
10918             result = o.reader.read(response);
10919         }catch(e){
10920             this.fireEvent("loadexception", this, o, response, e);
10921             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10922             return;
10923         }
10924         
10925         this.fireEvent("load", this, o, o.request.arg);
10926         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10927     },
10928
10929     // private
10930     update : function(dataSet){
10931
10932     },
10933
10934     // private
10935     updateResponse : function(dataSet){
10936
10937     }
10938 });/*
10939  * Based on:
10940  * Ext JS Library 1.1.1
10941  * Copyright(c) 2006-2007, Ext JS, LLC.
10942  *
10943  * Originally Released Under LGPL - original licence link has changed is not relivant.
10944  *
10945  * Fork - LGPL
10946  * <script type="text/javascript">
10947  */
10948
10949 /**
10950  * @class Roo.data.ScriptTagProxy
10951  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10952  * other than the originating domain of the running page.<br><br>
10953  * <p>
10954  * <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
10955  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10956  * <p>
10957  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10958  * source code that is used as the source inside a &lt;script> tag.<br><br>
10959  * <p>
10960  * In order for the browser to process the returned data, the server must wrap the data object
10961  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10962  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10963  * depending on whether the callback name was passed:
10964  * <p>
10965  * <pre><code>
10966 boolean scriptTag = false;
10967 String cb = request.getParameter("callback");
10968 if (cb != null) {
10969     scriptTag = true;
10970     response.setContentType("text/javascript");
10971 } else {
10972     response.setContentType("application/x-json");
10973 }
10974 Writer out = response.getWriter();
10975 if (scriptTag) {
10976     out.write(cb + "(");
10977 }
10978 out.print(dataBlock.toJsonString());
10979 if (scriptTag) {
10980     out.write(");");
10981 }
10982 </pre></code>
10983  *
10984  * @constructor
10985  * @param {Object} config A configuration object.
10986  */
10987 Roo.data.ScriptTagProxy = function(config){
10988     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10989     Roo.apply(this, config);
10990     this.head = document.getElementsByTagName("head")[0];
10991 };
10992
10993 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10994
10995 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10996     /**
10997      * @cfg {String} url The URL from which to request the data object.
10998      */
10999     /**
11000      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11001      */
11002     timeout : 30000,
11003     /**
11004      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11005      * the server the name of the callback function set up by the load call to process the returned data object.
11006      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11007      * javascript output which calls this named function passing the data object as its only parameter.
11008      */
11009     callbackParam : "callback",
11010     /**
11011      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11012      * name to the request.
11013      */
11014     nocache : true,
11015
11016     /**
11017      * Load data from the configured URL, read the data object into
11018      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11019      * process that block using the passed callback.
11020      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11021      * for the request to the remote server.
11022      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11023      * object into a block of Roo.data.Records.
11024      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11025      * The function must be passed <ul>
11026      * <li>The Record block object</li>
11027      * <li>The "arg" argument from the load function</li>
11028      * <li>A boolean success indicator</li>
11029      * </ul>
11030      * @param {Object} scope The scope in which to call the callback
11031      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11032      */
11033     load : function(params, reader, callback, scope, arg){
11034         if(this.fireEvent("beforeload", this, params) !== false){
11035
11036             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11037
11038             var url = this.url;
11039             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11040             if(this.nocache){
11041                 url += "&_dc=" + (new Date().getTime());
11042             }
11043             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11044             var trans = {
11045                 id : transId,
11046                 cb : "stcCallback"+transId,
11047                 scriptId : "stcScript"+transId,
11048                 params : params,
11049                 arg : arg,
11050                 url : url,
11051                 callback : callback,
11052                 scope : scope,
11053                 reader : reader
11054             };
11055             var conn = this;
11056
11057             window[trans.cb] = function(o){
11058                 conn.handleResponse(o, trans);
11059             };
11060
11061             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11062
11063             if(this.autoAbort !== false){
11064                 this.abort();
11065             }
11066
11067             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11068
11069             var script = document.createElement("script");
11070             script.setAttribute("src", url);
11071             script.setAttribute("type", "text/javascript");
11072             script.setAttribute("id", trans.scriptId);
11073             this.head.appendChild(script);
11074
11075             this.trans = trans;
11076         }else{
11077             callback.call(scope||this, null, arg, false);
11078         }
11079     },
11080
11081     // private
11082     isLoading : function(){
11083         return this.trans ? true : false;
11084     },
11085
11086     /**
11087      * Abort the current server request.
11088      */
11089     abort : function(){
11090         if(this.isLoading()){
11091             this.destroyTrans(this.trans);
11092         }
11093     },
11094
11095     // private
11096     destroyTrans : function(trans, isLoaded){
11097         this.head.removeChild(document.getElementById(trans.scriptId));
11098         clearTimeout(trans.timeoutId);
11099         if(isLoaded){
11100             window[trans.cb] = undefined;
11101             try{
11102                 delete window[trans.cb];
11103             }catch(e){}
11104         }else{
11105             // if hasn't been loaded, wait for load to remove it to prevent script error
11106             window[trans.cb] = function(){
11107                 window[trans.cb] = undefined;
11108                 try{
11109                     delete window[trans.cb];
11110                 }catch(e){}
11111             };
11112         }
11113     },
11114
11115     // private
11116     handleResponse : function(o, trans){
11117         this.trans = false;
11118         this.destroyTrans(trans, true);
11119         var result;
11120         try {
11121             result = trans.reader.readRecords(o);
11122         }catch(e){
11123             this.fireEvent("loadexception", this, o, trans.arg, e);
11124             trans.callback.call(trans.scope||window, null, trans.arg, false);
11125             return;
11126         }
11127         this.fireEvent("load", this, o, trans.arg);
11128         trans.callback.call(trans.scope||window, result, trans.arg, true);
11129     },
11130
11131     // private
11132     handleFailure : function(trans){
11133         this.trans = false;
11134         this.destroyTrans(trans, false);
11135         this.fireEvent("loadexception", this, null, trans.arg);
11136         trans.callback.call(trans.scope||window, null, trans.arg, false);
11137     }
11138 });/*
11139  * Based on:
11140  * Ext JS Library 1.1.1
11141  * Copyright(c) 2006-2007, Ext JS, LLC.
11142  *
11143  * Originally Released Under LGPL - original licence link has changed is not relivant.
11144  *
11145  * Fork - LGPL
11146  * <script type="text/javascript">
11147  */
11148
11149 /**
11150  * @class Roo.data.JsonReader
11151  * @extends Roo.data.DataReader
11152  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11153  * based on mappings in a provided Roo.data.Record constructor.
11154  * 
11155  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11156  * in the reply previously. 
11157  * 
11158  * <p>
11159  * Example code:
11160  * <pre><code>
11161 var RecordDef = Roo.data.Record.create([
11162     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11163     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11164 ]);
11165 var myReader = new Roo.data.JsonReader({
11166     totalProperty: "results",    // The property which contains the total dataset size (optional)
11167     root: "rows",                // The property which contains an Array of row objects
11168     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11169 }, RecordDef);
11170 </code></pre>
11171  * <p>
11172  * This would consume a JSON file like this:
11173  * <pre><code>
11174 { 'results': 2, 'rows': [
11175     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11176     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11177 }
11178 </code></pre>
11179  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11180  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11181  * paged from the remote server.
11182  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11183  * @cfg {String} root name of the property which contains the Array of row objects.
11184  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11185  * @cfg {Array} fields Array of field definition objects
11186  * @constructor
11187  * Create a new JsonReader
11188  * @param {Object} meta Metadata configuration options
11189  * @param {Object} recordType Either an Array of field definition objects,
11190  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11191  */
11192 Roo.data.JsonReader = function(meta, recordType){
11193     
11194     meta = meta || {};
11195     // set some defaults:
11196     Roo.applyIf(meta, {
11197         totalProperty: 'total',
11198         successProperty : 'success',
11199         root : 'data',
11200         id : 'id'
11201     });
11202     
11203     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11204 };
11205 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11206     
11207     /**
11208      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11209      * Used by Store query builder to append _requestMeta to params.
11210      * 
11211      */
11212     metaFromRemote : false,
11213     /**
11214      * This method is only used by a DataProxy which has retrieved data from a remote server.
11215      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11216      * @return {Object} data A data block which is used by an Roo.data.Store object as
11217      * a cache of Roo.data.Records.
11218      */
11219     read : function(response){
11220         var json = response.responseText;
11221        
11222         var o = /* eval:var:o */ eval("("+json+")");
11223         if(!o) {
11224             throw {message: "JsonReader.read: Json object not found"};
11225         }
11226         
11227         if(o.metaData){
11228             
11229             delete this.ef;
11230             this.metaFromRemote = true;
11231             this.meta = o.metaData;
11232             this.recordType = Roo.data.Record.create(o.metaData.fields);
11233             this.onMetaChange(this.meta, this.recordType, o);
11234         }
11235         return this.readRecords(o);
11236     },
11237
11238     // private function a store will implement
11239     onMetaChange : function(meta, recordType, o){
11240
11241     },
11242
11243     /**
11244          * @ignore
11245          */
11246     simpleAccess: function(obj, subsc) {
11247         return obj[subsc];
11248     },
11249
11250         /**
11251          * @ignore
11252          */
11253     getJsonAccessor: function(){
11254         var re = /[\[\.]/;
11255         return function(expr) {
11256             try {
11257                 return(re.test(expr))
11258                     ? new Function("obj", "return obj." + expr)
11259                     : function(obj){
11260                         return obj[expr];
11261                     };
11262             } catch(e){}
11263             return Roo.emptyFn;
11264         };
11265     }(),
11266
11267     /**
11268      * Create a data block containing Roo.data.Records from an XML document.
11269      * @param {Object} o An object which contains an Array of row objects in the property specified
11270      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11271      * which contains the total size of the dataset.
11272      * @return {Object} data A data block which is used by an Roo.data.Store object as
11273      * a cache of Roo.data.Records.
11274      */
11275     readRecords : function(o){
11276         /**
11277          * After any data loads, the raw JSON data is available for further custom processing.
11278          * @type Object
11279          */
11280         this.o = o;
11281         var s = this.meta, Record = this.recordType,
11282             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11283
11284 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11285         if (!this.ef) {
11286             if(s.totalProperty) {
11287                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11288                 }
11289                 if(s.successProperty) {
11290                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11291                 }
11292                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11293                 if (s.id) {
11294                         var g = this.getJsonAccessor(s.id);
11295                         this.getId = function(rec) {
11296                                 var r = g(rec);  
11297                                 return (r === undefined || r === "") ? null : r;
11298                         };
11299                 } else {
11300                         this.getId = function(){return null;};
11301                 }
11302             this.ef = [];
11303             for(var jj = 0; jj < fl; jj++){
11304                 f = fi[jj];
11305                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11306                 this.ef[jj] = this.getJsonAccessor(map);
11307             }
11308         }
11309
11310         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11311         if(s.totalProperty){
11312             var vt = parseInt(this.getTotal(o), 10);
11313             if(!isNaN(vt)){
11314                 totalRecords = vt;
11315             }
11316         }
11317         if(s.successProperty){
11318             var vs = this.getSuccess(o);
11319             if(vs === false || vs === 'false'){
11320                 success = false;
11321             }
11322         }
11323         var records = [];
11324         for(var i = 0; i < c; i++){
11325                 var n = root[i];
11326             var values = {};
11327             var id = this.getId(n);
11328             for(var j = 0; j < fl; j++){
11329                 f = fi[j];
11330             var v = this.ef[j](n);
11331             if (!f.convert) {
11332                 Roo.log('missing convert for ' + f.name);
11333                 Roo.log(f);
11334                 continue;
11335             }
11336             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11337             }
11338             var record = new Record(values, id);
11339             record.json = n;
11340             records[i] = record;
11341         }
11342         return {
11343             raw : o,
11344             success : success,
11345             records : records,
11346             totalRecords : totalRecords
11347         };
11348     }
11349 });/*
11350  * Based on:
11351  * Ext JS Library 1.1.1
11352  * Copyright(c) 2006-2007, Ext JS, LLC.
11353  *
11354  * Originally Released Under LGPL - original licence link has changed is not relivant.
11355  *
11356  * Fork - LGPL
11357  * <script type="text/javascript">
11358  */
11359
11360 /**
11361  * @class Roo.data.ArrayReader
11362  * @extends Roo.data.DataReader
11363  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11364  * Each element of that Array represents a row of data fields. The
11365  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11366  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11367  * <p>
11368  * Example code:.
11369  * <pre><code>
11370 var RecordDef = Roo.data.Record.create([
11371     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11372     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11373 ]);
11374 var myReader = new Roo.data.ArrayReader({
11375     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11376 }, RecordDef);
11377 </code></pre>
11378  * <p>
11379  * This would consume an Array like this:
11380  * <pre><code>
11381 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11382   </code></pre>
11383  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11384  * @constructor
11385  * Create a new JsonReader
11386  * @param {Object} meta Metadata configuration options.
11387  * @param {Object} recordType Either an Array of field definition objects
11388  * as specified to {@link Roo.data.Record#create},
11389  * or an {@link Roo.data.Record} object
11390  * created using {@link Roo.data.Record#create}.
11391  */
11392 Roo.data.ArrayReader = function(meta, recordType){
11393     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11394 };
11395
11396 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11397     /**
11398      * Create a data block containing Roo.data.Records from an XML document.
11399      * @param {Object} o An Array of row objects which represents the dataset.
11400      * @return {Object} data A data block which is used by an Roo.data.Store object as
11401      * a cache of Roo.data.Records.
11402      */
11403     readRecords : function(o){
11404         var sid = this.meta ? this.meta.id : null;
11405         var recordType = this.recordType, fields = recordType.prototype.fields;
11406         var records = [];
11407         var root = o;
11408             for(var i = 0; i < root.length; i++){
11409                     var n = root[i];
11410                 var values = {};
11411                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11412                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11413                 var f = fields.items[j];
11414                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11415                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11416                 v = f.convert(v);
11417                 values[f.name] = v;
11418             }
11419                 var record = new recordType(values, id);
11420                 record.json = n;
11421                 records[records.length] = record;
11422             }
11423             return {
11424                 records : records,
11425                 totalRecords : records.length
11426             };
11427     }
11428 });/*
11429  * - LGPL
11430  * * 
11431  */
11432
11433 /**
11434  * @class Roo.bootstrap.ComboBox
11435  * @extends Roo.bootstrap.TriggerField
11436  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11437  * @cfg {Boolean} append (true|false) default false
11438  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11439  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11440  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11441  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11442  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11443  * @cfg {Boolean} animate default true
11444  * @cfg {Boolean} emptyResultText only for touch device
11445  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11446  * @constructor
11447  * Create a new ComboBox.
11448  * @param {Object} config Configuration options
11449  */
11450 Roo.bootstrap.ComboBox = function(config){
11451     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11452     this.addEvents({
11453         /**
11454          * @event expand
11455          * Fires when the dropdown list is expanded
11456              * @param {Roo.bootstrap.ComboBox} combo This combo box
11457              */
11458         'expand' : true,
11459         /**
11460          * @event collapse
11461          * Fires when the dropdown list is collapsed
11462              * @param {Roo.bootstrap.ComboBox} combo This combo box
11463              */
11464         'collapse' : true,
11465         /**
11466          * @event beforeselect
11467          * Fires before a list item is selected. Return false to cancel the selection.
11468              * @param {Roo.bootstrap.ComboBox} combo This combo box
11469              * @param {Roo.data.Record} record The data record returned from the underlying store
11470              * @param {Number} index The index of the selected item in the dropdown list
11471              */
11472         'beforeselect' : true,
11473         /**
11474          * @event select
11475          * Fires when a list item is selected
11476              * @param {Roo.bootstrap.ComboBox} combo This combo box
11477              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11478              * @param {Number} index The index of the selected item in the dropdown list
11479              */
11480         'select' : true,
11481         /**
11482          * @event beforequery
11483          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11484          * The event object passed has these properties:
11485              * @param {Roo.bootstrap.ComboBox} combo This combo box
11486              * @param {String} query The query
11487              * @param {Boolean} forceAll true to force "all" query
11488              * @param {Boolean} cancel true to cancel the query
11489              * @param {Object} e The query event object
11490              */
11491         'beforequery': true,
11492          /**
11493          * @event add
11494          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11495              * @param {Roo.bootstrap.ComboBox} combo This combo box
11496              */
11497         'add' : true,
11498         /**
11499          * @event edit
11500          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11501              * @param {Roo.bootstrap.ComboBox} combo This combo box
11502              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11503              */
11504         'edit' : true,
11505         /**
11506          * @event remove
11507          * Fires when the remove value from the combobox array
11508              * @param {Roo.bootstrap.ComboBox} combo This combo box
11509              */
11510         'remove' : true,
11511         /**
11512          * @event specialfilter
11513          * Fires when specialfilter
11514             * @param {Roo.bootstrap.ComboBox} combo This combo box
11515             */
11516         'specialfilter' : true,
11517         /**
11518          * @event tick
11519          * Fires when tick the element
11520             * @param {Roo.bootstrap.ComboBox} combo This combo box
11521             */
11522         'tick' : true,
11523         /**
11524          * @event touchviewdisplay
11525          * Fires when touch view require special display (default is using displayField)
11526             * @param {Roo.bootstrap.ComboBox} combo This combo box
11527             * @param {Object} cfg set html .
11528             */
11529         'touchviewdisplay' : true
11530         
11531     });
11532     
11533     this.item = [];
11534     this.tickItems = [];
11535     
11536     this.selectedIndex = -1;
11537     if(this.mode == 'local'){
11538         if(config.queryDelay === undefined){
11539             this.queryDelay = 10;
11540         }
11541         if(config.minChars === undefined){
11542             this.minChars = 0;
11543         }
11544     }
11545 };
11546
11547 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11548      
11549     /**
11550      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11551      * rendering into an Roo.Editor, defaults to false)
11552      */
11553     /**
11554      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11555      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11556      */
11557     /**
11558      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11559      */
11560     /**
11561      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11562      * the dropdown list (defaults to undefined, with no header element)
11563      */
11564
11565      /**
11566      * @cfg {String/Roo.Template} tpl The template to use to render the output
11567      */
11568      
11569      /**
11570      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11571      */
11572     listWidth: undefined,
11573     /**
11574      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11575      * mode = 'remote' or 'text' if mode = 'local')
11576      */
11577     displayField: undefined,
11578     
11579     /**
11580      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11581      * mode = 'remote' or 'value' if mode = 'local'). 
11582      * Note: use of a valueField requires the user make a selection
11583      * in order for a value to be mapped.
11584      */
11585     valueField: undefined,
11586     
11587     
11588     /**
11589      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11590      * field's data value (defaults to the underlying DOM element's name)
11591      */
11592     hiddenName: undefined,
11593     /**
11594      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11595      */
11596     listClass: '',
11597     /**
11598      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11599      */
11600     selectedClass: 'active',
11601     
11602     /**
11603      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11604      */
11605     shadow:'sides',
11606     /**
11607      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11608      * anchor positions (defaults to 'tl-bl')
11609      */
11610     listAlign: 'tl-bl?',
11611     /**
11612      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11613      */
11614     maxHeight: 300,
11615     /**
11616      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11617      * query specified by the allQuery config option (defaults to 'query')
11618      */
11619     triggerAction: 'query',
11620     /**
11621      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11622      * (defaults to 4, does not apply if editable = false)
11623      */
11624     minChars : 4,
11625     /**
11626      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11627      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11628      */
11629     typeAhead: false,
11630     /**
11631      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11632      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11633      */
11634     queryDelay: 500,
11635     /**
11636      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11637      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11638      */
11639     pageSize: 0,
11640     /**
11641      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11642      * when editable = true (defaults to false)
11643      */
11644     selectOnFocus:false,
11645     /**
11646      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11647      */
11648     queryParam: 'query',
11649     /**
11650      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11651      * when mode = 'remote' (defaults to 'Loading...')
11652      */
11653     loadingText: 'Loading...',
11654     /**
11655      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11656      */
11657     resizable: false,
11658     /**
11659      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11660      */
11661     handleHeight : 8,
11662     /**
11663      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11664      * traditional select (defaults to true)
11665      */
11666     editable: true,
11667     /**
11668      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11669      */
11670     allQuery: '',
11671     /**
11672      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11673      */
11674     mode: 'remote',
11675     /**
11676      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11677      * listWidth has a higher value)
11678      */
11679     minListWidth : 70,
11680     /**
11681      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11682      * allow the user to set arbitrary text into the field (defaults to false)
11683      */
11684     forceSelection:false,
11685     /**
11686      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11687      * if typeAhead = true (defaults to 250)
11688      */
11689     typeAheadDelay : 250,
11690     /**
11691      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11692      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11693      */
11694     valueNotFoundText : undefined,
11695     /**
11696      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11697      */
11698     blockFocus : false,
11699     
11700     /**
11701      * @cfg {Boolean} disableClear Disable showing of clear button.
11702      */
11703     disableClear : false,
11704     /**
11705      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11706      */
11707     alwaysQuery : false,
11708     
11709     /**
11710      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11711      */
11712     multiple : false,
11713     
11714     /**
11715      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11716      */
11717     invalidClass : "has-warning",
11718     
11719     /**
11720      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11721      */
11722     validClass : "has-success",
11723     
11724     /**
11725      * @cfg {Boolean} specialFilter (true|false) special filter default false
11726      */
11727     specialFilter : false,
11728     
11729     /**
11730      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11731      */
11732     mobileTouchView : true,
11733     
11734     //private
11735     addicon : false,
11736     editicon: false,
11737     
11738     page: 0,
11739     hasQuery: false,
11740     append: false,
11741     loadNext: false,
11742     autoFocus : true,
11743     tickable : false,
11744     btnPosition : 'right',
11745     triggerList : true,
11746     showToggleBtn : true,
11747     animate : true,
11748     emptyResultText: 'Empty',
11749     triggerText : 'Select',
11750     
11751     // element that contains real text value.. (when hidden is used..)
11752     
11753     getAutoCreate : function()
11754     {
11755         var cfg = false;
11756         
11757         /*
11758          * Touch Devices
11759          */
11760         
11761         if(Roo.isTouch && this.mobileTouchView){
11762             cfg = this.getAutoCreateTouchView();
11763             return cfg;;
11764         }
11765         
11766         /*
11767          *  Normal ComboBox
11768          */
11769         if(!this.tickable){
11770             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11771             return cfg;
11772         }
11773         
11774         /*
11775          *  ComboBox with tickable selections
11776          */
11777              
11778         var align = this.labelAlign || this.parentLabelAlign();
11779         
11780         cfg = {
11781             cls : 'form-group roo-combobox-tickable' //input-group
11782         };
11783         
11784         var buttons = {
11785             tag : 'div',
11786             cls : 'tickable-buttons',
11787             cn : [
11788                 {
11789                     tag : 'button',
11790                     type : 'button',
11791                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11792                     html : this.triggerText
11793                 },
11794                 {
11795                     tag : 'button',
11796                     type : 'button',
11797                     name : 'ok',
11798                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11799                     html : 'Done'
11800                 },
11801                 {
11802                     tag : 'button',
11803                     type : 'button',
11804                     name : 'cancel',
11805                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11806                     html : 'Cancel'
11807                 }
11808             ]
11809         };
11810         
11811         if(this.editable){
11812             buttons.cn.unshift({
11813                 tag: 'input',
11814                 cls: 'select2-search-field-input'
11815             });
11816         }
11817         
11818         var _this = this;
11819         
11820         Roo.each(buttons.cn, function(c){
11821             if (_this.size) {
11822                 c.cls += ' btn-' + _this.size;
11823             }
11824
11825             if (_this.disabled) {
11826                 c.disabled = true;
11827             }
11828         });
11829         
11830         var box = {
11831             tag: 'div',
11832             cn: [
11833                 {
11834                     tag: 'input',
11835                     type : 'hidden',
11836                     cls: 'form-hidden-field'
11837                 },
11838                 {
11839                     tag: 'ul',
11840                     cls: 'select2-choices',
11841                     cn:[
11842                         {
11843                             tag: 'li',
11844                             cls: 'select2-search-field',
11845                             cn: [
11846
11847                                 buttons
11848                             ]
11849                         }
11850                     ]
11851                 }
11852             ]
11853         };
11854         
11855         var combobox = {
11856             cls: 'select2-container input-group select2-container-multi',
11857             cn: [
11858                 box
11859 //                {
11860 //                    tag: 'ul',
11861 //                    cls: 'typeahead typeahead-long dropdown-menu',
11862 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11863 //                }
11864             ]
11865         };
11866         
11867         if(this.hasFeedback && !this.allowBlank){
11868             
11869             var feedback = {
11870                 tag: 'span',
11871                 cls: 'glyphicon form-control-feedback'
11872             };
11873
11874             combobox.cn.push(feedback);
11875         }
11876         
11877         if (align ==='left' && this.fieldLabel.length) {
11878             
11879 //                Roo.log("left and has label");
11880                 cfg.cn = [
11881                     
11882                     {
11883                         tag: 'label',
11884                         'for' :  id,
11885                         cls : 'control-label col-sm-' + this.labelWidth,
11886                         html : this.fieldLabel
11887                         
11888                     },
11889                     {
11890                         cls : "col-sm-" + (12 - this.labelWidth), 
11891                         cn: [
11892                             combobox
11893                         ]
11894                     }
11895                     
11896                 ];
11897         } else if ( this.fieldLabel.length) {
11898 //                Roo.log(" label");
11899                  cfg.cn = [
11900                    
11901                     {
11902                         tag: 'label',
11903                         //cls : 'input-group-addon',
11904                         html : this.fieldLabel
11905                         
11906                     },
11907                     
11908                     combobox
11909                     
11910                 ];
11911
11912         } else {
11913             
11914 //                Roo.log(" no label && no align");
11915                 cfg = combobox
11916                      
11917                 
11918         }
11919          
11920         var settings=this;
11921         ['xs','sm','md','lg'].map(function(size){
11922             if (settings[size]) {
11923                 cfg.cls += ' col-' + size + '-' + settings[size];
11924             }
11925         });
11926         
11927         return cfg;
11928         
11929     },
11930     
11931     _initEventsCalled : false,
11932     
11933     // private
11934     initEvents: function()
11935     {
11936         
11937         if (this._initEventsCalled) { // as we call render... prevent looping...
11938             return;
11939         }
11940         this._initEventsCalled = true;
11941         
11942         if (!this.store) {
11943             throw "can not find store for combo";
11944         }
11945         
11946         this.store = Roo.factory(this.store, Roo.data);
11947         
11948         // if we are building from html. then this element is so complex, that we can not really
11949         // use the rendered HTML.
11950         // so we have to trash and replace the previous code.
11951         if (Roo.XComponent.build_from_html) {
11952             
11953             // remove this element....
11954             var e = this.el.dom, k=0;
11955             while (e ) { e = e.previousSibling;  ++k;}
11956
11957             this.el.remove();
11958             
11959             this.el=false;
11960             this.rendered = false;
11961             
11962             this.render(this.parent().getChildContainer(true), k);
11963             
11964             
11965             
11966         }
11967         
11968         
11969         /*
11970          * Touch Devices
11971          */
11972         
11973         if(Roo.isTouch && this.mobileTouchView){
11974             this.initTouchView();
11975             return;
11976         }
11977         
11978         if(this.tickable){
11979             this.initTickableEvents();
11980             return;
11981         }
11982         
11983         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11984         
11985         if(this.hiddenName){
11986             
11987             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11988             
11989             this.hiddenField.dom.value =
11990                 this.hiddenValue !== undefined ? this.hiddenValue :
11991                 this.value !== undefined ? this.value : '';
11992
11993             // prevent input submission
11994             this.el.dom.removeAttribute('name');
11995             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11996              
11997              
11998         }
11999         //if(Roo.isGecko){
12000         //    this.el.dom.setAttribute('autocomplete', 'off');
12001         //}
12002         
12003         var cls = 'x-combo-list';
12004         
12005         //this.list = new Roo.Layer({
12006         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12007         //});
12008         
12009         var _this = this;
12010         
12011         (function(){
12012             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12013             _this.list.setWidth(lw);
12014         }).defer(100);
12015         
12016         this.list.on('mouseover', this.onViewOver, this);
12017         this.list.on('mousemove', this.onViewMove, this);
12018         
12019         this.list.on('scroll', this.onViewScroll, this);
12020         
12021         /*
12022         this.list.swallowEvent('mousewheel');
12023         this.assetHeight = 0;
12024
12025         if(this.title){
12026             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12027             this.assetHeight += this.header.getHeight();
12028         }
12029
12030         this.innerList = this.list.createChild({cls:cls+'-inner'});
12031         this.innerList.on('mouseover', this.onViewOver, this);
12032         this.innerList.on('mousemove', this.onViewMove, this);
12033         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12034         
12035         if(this.allowBlank && !this.pageSize && !this.disableClear){
12036             this.footer = this.list.createChild({cls:cls+'-ft'});
12037             this.pageTb = new Roo.Toolbar(this.footer);
12038            
12039         }
12040         if(this.pageSize){
12041             this.footer = this.list.createChild({cls:cls+'-ft'});
12042             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12043                     {pageSize: this.pageSize});
12044             
12045         }
12046         
12047         if (this.pageTb && this.allowBlank && !this.disableClear) {
12048             var _this = this;
12049             this.pageTb.add(new Roo.Toolbar.Fill(), {
12050                 cls: 'x-btn-icon x-btn-clear',
12051                 text: '&#160;',
12052                 handler: function()
12053                 {
12054                     _this.collapse();
12055                     _this.clearValue();
12056                     _this.onSelect(false, -1);
12057                 }
12058             });
12059         }
12060         if (this.footer) {
12061             this.assetHeight += this.footer.getHeight();
12062         }
12063         */
12064             
12065         if(!this.tpl){
12066             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12067         }
12068
12069         this.view = new Roo.View(this.list, this.tpl, {
12070             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12071         });
12072         //this.view.wrapEl.setDisplayed(false);
12073         this.view.on('click', this.onViewClick, this);
12074         
12075         
12076         
12077         this.store.on('beforeload', this.onBeforeLoad, this);
12078         this.store.on('load', this.onLoad, this);
12079         this.store.on('loadexception', this.onLoadException, this);
12080         /*
12081         if(this.resizable){
12082             this.resizer = new Roo.Resizable(this.list,  {
12083                pinned:true, handles:'se'
12084             });
12085             this.resizer.on('resize', function(r, w, h){
12086                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12087                 this.listWidth = w;
12088                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12089                 this.restrictHeight();
12090             }, this);
12091             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12092         }
12093         */
12094         if(!this.editable){
12095             this.editable = true;
12096             this.setEditable(false);
12097         }
12098         
12099         /*
12100         
12101         if (typeof(this.events.add.listeners) != 'undefined') {
12102             
12103             this.addicon = this.wrap.createChild(
12104                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12105        
12106             this.addicon.on('click', function(e) {
12107                 this.fireEvent('add', this);
12108             }, this);
12109         }
12110         if (typeof(this.events.edit.listeners) != 'undefined') {
12111             
12112             this.editicon = this.wrap.createChild(
12113                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12114             if (this.addicon) {
12115                 this.editicon.setStyle('margin-left', '40px');
12116             }
12117             this.editicon.on('click', function(e) {
12118                 
12119                 // we fire even  if inothing is selected..
12120                 this.fireEvent('edit', this, this.lastData );
12121                 
12122             }, this);
12123         }
12124         */
12125         
12126         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12127             "up" : function(e){
12128                 this.inKeyMode = true;
12129                 this.selectPrev();
12130             },
12131
12132             "down" : function(e){
12133                 if(!this.isExpanded()){
12134                     this.onTriggerClick();
12135                 }else{
12136                     this.inKeyMode = true;
12137                     this.selectNext();
12138                 }
12139             },
12140
12141             "enter" : function(e){
12142 //                this.onViewClick();
12143                 //return true;
12144                 this.collapse();
12145                 
12146                 if(this.fireEvent("specialkey", this, e)){
12147                     this.onViewClick(false);
12148                 }
12149                 
12150                 return true;
12151             },
12152
12153             "esc" : function(e){
12154                 this.collapse();
12155             },
12156
12157             "tab" : function(e){
12158                 this.collapse();
12159                 
12160                 if(this.fireEvent("specialkey", this, e)){
12161                     this.onViewClick(false);
12162                 }
12163                 
12164                 return true;
12165             },
12166
12167             scope : this,
12168
12169             doRelay : function(foo, bar, hname){
12170                 if(hname == 'down' || this.scope.isExpanded()){
12171                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12172                 }
12173                 return true;
12174             },
12175
12176             forceKeyDown: true
12177         });
12178         
12179         
12180         this.queryDelay = Math.max(this.queryDelay || 10,
12181                 this.mode == 'local' ? 10 : 250);
12182         
12183         
12184         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12185         
12186         if(this.typeAhead){
12187             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12188         }
12189         if(this.editable !== false){
12190             this.inputEl().on("keyup", this.onKeyUp, this);
12191         }
12192         if(this.forceSelection){
12193             this.inputEl().on('blur', this.doForce, this);
12194         }
12195         
12196         if(this.multiple){
12197             this.choices = this.el.select('ul.select2-choices', true).first();
12198             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12199         }
12200     },
12201     
12202     initTickableEvents: function()
12203     {   
12204         this.createList();
12205         
12206         if(this.hiddenName){
12207             
12208             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12209             
12210             this.hiddenField.dom.value =
12211                 this.hiddenValue !== undefined ? this.hiddenValue :
12212                 this.value !== undefined ? this.value : '';
12213
12214             // prevent input submission
12215             this.el.dom.removeAttribute('name');
12216             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12217              
12218              
12219         }
12220         
12221 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12222         
12223         this.choices = this.el.select('ul.select2-choices', true).first();
12224         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12225         if(this.triggerList){
12226             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12227         }
12228          
12229         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12230         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12231         
12232         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12233         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12234         
12235         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12236         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12237         
12238         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12239         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12240         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12241         
12242         this.okBtn.hide();
12243         this.cancelBtn.hide();
12244         
12245         var _this = this;
12246         
12247         (function(){
12248             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12249             _this.list.setWidth(lw);
12250         }).defer(100);
12251         
12252         this.list.on('mouseover', this.onViewOver, this);
12253         this.list.on('mousemove', this.onViewMove, this);
12254         
12255         this.list.on('scroll', this.onViewScroll, this);
12256         
12257         if(!this.tpl){
12258             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12259         }
12260
12261         this.view = new Roo.View(this.list, this.tpl, {
12262             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12263         });
12264         
12265         //this.view.wrapEl.setDisplayed(false);
12266         this.view.on('click', this.onViewClick, this);
12267         
12268         
12269         
12270         this.store.on('beforeload', this.onBeforeLoad, this);
12271         this.store.on('load', this.onLoad, this);
12272         this.store.on('loadexception', this.onLoadException, this);
12273         
12274         if(this.editable){
12275             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12276                 "up" : function(e){
12277                     this.inKeyMode = true;
12278                     this.selectPrev();
12279                 },
12280
12281                 "down" : function(e){
12282                     this.inKeyMode = true;
12283                     this.selectNext();
12284                 },
12285
12286                 "enter" : function(e){
12287                     if(this.fireEvent("specialkey", this, e)){
12288                         this.onViewClick(false);
12289                     }
12290                     
12291                     return true;
12292                 },
12293
12294                 "esc" : function(e){
12295                     this.onTickableFooterButtonClick(e, false, false);
12296                 },
12297
12298                 "tab" : function(e){
12299                     this.fireEvent("specialkey", this, e);
12300                     
12301                     this.onTickableFooterButtonClick(e, false, false);
12302                     
12303                     return true;
12304                 },
12305
12306                 scope : this,
12307
12308                 doRelay : function(e, fn, key){
12309                     if(this.scope.isExpanded()){
12310                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12311                     }
12312                     return true;
12313                 },
12314
12315                 forceKeyDown: true
12316             });
12317         }
12318         
12319         this.queryDelay = Math.max(this.queryDelay || 10,
12320                 this.mode == 'local' ? 10 : 250);
12321         
12322         
12323         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12324         
12325         if(this.typeAhead){
12326             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12327         }
12328         
12329         if(this.editable !== false){
12330             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12331         }
12332         
12333     },
12334
12335     onDestroy : function(){
12336         if(this.view){
12337             this.view.setStore(null);
12338             this.view.el.removeAllListeners();
12339             this.view.el.remove();
12340             this.view.purgeListeners();
12341         }
12342         if(this.list){
12343             this.list.dom.innerHTML  = '';
12344         }
12345         
12346         if(this.store){
12347             this.store.un('beforeload', this.onBeforeLoad, this);
12348             this.store.un('load', this.onLoad, this);
12349             this.store.un('loadexception', this.onLoadException, this);
12350         }
12351         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12352     },
12353
12354     // private
12355     fireKey : function(e){
12356         if(e.isNavKeyPress() && !this.list.isVisible()){
12357             this.fireEvent("specialkey", this, e);
12358         }
12359     },
12360
12361     // private
12362     onResize: function(w, h){
12363 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12364 //        
12365 //        if(typeof w != 'number'){
12366 //            // we do not handle it!?!?
12367 //            return;
12368 //        }
12369 //        var tw = this.trigger.getWidth();
12370 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12371 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12372 //        var x = w - tw;
12373 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12374 //            
12375 //        //this.trigger.setStyle('left', x+'px');
12376 //        
12377 //        if(this.list && this.listWidth === undefined){
12378 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12379 //            this.list.setWidth(lw);
12380 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12381 //        }
12382         
12383     
12384         
12385     },
12386
12387     /**
12388      * Allow or prevent the user from directly editing the field text.  If false is passed,
12389      * the user will only be able to select from the items defined in the dropdown list.  This method
12390      * is the runtime equivalent of setting the 'editable' config option at config time.
12391      * @param {Boolean} value True to allow the user to directly edit the field text
12392      */
12393     setEditable : function(value){
12394         if(value == this.editable){
12395             return;
12396         }
12397         this.editable = value;
12398         if(!value){
12399             this.inputEl().dom.setAttribute('readOnly', true);
12400             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12401             this.inputEl().addClass('x-combo-noedit');
12402         }else{
12403             this.inputEl().dom.setAttribute('readOnly', false);
12404             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12405             this.inputEl().removeClass('x-combo-noedit');
12406         }
12407     },
12408
12409     // private
12410     
12411     onBeforeLoad : function(combo,opts){
12412         if(!this.hasFocus){
12413             return;
12414         }
12415          if (!opts.add) {
12416             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12417          }
12418         this.restrictHeight();
12419         this.selectedIndex = -1;
12420     },
12421
12422     // private
12423     onLoad : function(){
12424         
12425         this.hasQuery = false;
12426         
12427         if(!this.hasFocus){
12428             return;
12429         }
12430         
12431         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12432             this.loading.hide();
12433         }
12434              
12435         if(this.store.getCount() > 0){
12436             this.expand();
12437             this.restrictHeight();
12438             if(this.lastQuery == this.allQuery){
12439                 if(this.editable && !this.tickable){
12440                     this.inputEl().dom.select();
12441                 }
12442                 
12443                 if(
12444                     !this.selectByValue(this.value, true) &&
12445                     this.autoFocus && 
12446                     (
12447                         !this.store.lastOptions ||
12448                         typeof(this.store.lastOptions.add) == 'undefined' || 
12449                         this.store.lastOptions.add != true
12450                     )
12451                 ){
12452                     this.select(0, true);
12453                 }
12454             }else{
12455                 if(this.autoFocus){
12456                     this.selectNext();
12457                 }
12458                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12459                     this.taTask.delay(this.typeAheadDelay);
12460                 }
12461             }
12462         }else{
12463             this.onEmptyResults();
12464         }
12465         
12466         //this.el.focus();
12467     },
12468     // private
12469     onLoadException : function()
12470     {
12471         this.hasQuery = false;
12472         
12473         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12474             this.loading.hide();
12475         }
12476         
12477         if(this.tickable && this.editable){
12478             return;
12479         }
12480         
12481         this.collapse();
12482         // only causes errors at present
12483         //Roo.log(this.store.reader.jsonData);
12484         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12485             // fixme
12486             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12487         //}
12488         
12489         
12490     },
12491     // private
12492     onTypeAhead : function(){
12493         if(this.store.getCount() > 0){
12494             var r = this.store.getAt(0);
12495             var newValue = r.data[this.displayField];
12496             var len = newValue.length;
12497             var selStart = this.getRawValue().length;
12498             
12499             if(selStart != len){
12500                 this.setRawValue(newValue);
12501                 this.selectText(selStart, newValue.length);
12502             }
12503         }
12504     },
12505
12506     // private
12507     onSelect : function(record, index){
12508         
12509         if(this.fireEvent('beforeselect', this, record, index) !== false){
12510         
12511             this.setFromData(index > -1 ? record.data : false);
12512             
12513             this.collapse();
12514             this.fireEvent('select', this, record, index);
12515         }
12516     },
12517
12518     /**
12519      * Returns the currently selected field value or empty string if no value is set.
12520      * @return {String} value The selected value
12521      */
12522     getValue : function(){
12523         
12524         if(this.multiple){
12525             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12526         }
12527         
12528         if(this.valueField){
12529             return typeof this.value != 'undefined' ? this.value : '';
12530         }else{
12531             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12532         }
12533     },
12534
12535     /**
12536      * Clears any text/value currently set in the field
12537      */
12538     clearValue : function(){
12539         if(this.hiddenField){
12540             this.hiddenField.dom.value = '';
12541         }
12542         this.value = '';
12543         this.setRawValue('');
12544         this.lastSelectionText = '';
12545         this.lastData = false;
12546         
12547         var close = this.closeTriggerEl();
12548         
12549         if(close){
12550             close.hide();
12551         }
12552         
12553     },
12554
12555     /**
12556      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12557      * will be displayed in the field.  If the value does not match the data value of an existing item,
12558      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12559      * Otherwise the field will be blank (although the value will still be set).
12560      * @param {String} value The value to match
12561      */
12562     setValue : function(v){
12563         if(this.multiple){
12564             this.syncValue();
12565             return;
12566         }
12567         
12568         var text = v;
12569         if(this.valueField){
12570             var r = this.findRecord(this.valueField, v);
12571             if(r){
12572                 text = r.data[this.displayField];
12573             }else if(this.valueNotFoundText !== undefined){
12574                 text = this.valueNotFoundText;
12575             }
12576         }
12577         this.lastSelectionText = text;
12578         if(this.hiddenField){
12579             this.hiddenField.dom.value = v;
12580         }
12581         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12582         this.value = v;
12583         
12584         var close = this.closeTriggerEl();
12585         
12586         if(close){
12587             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12588         }
12589     },
12590     /**
12591      * @property {Object} the last set data for the element
12592      */
12593     
12594     lastData : false,
12595     /**
12596      * Sets the value of the field based on a object which is related to the record format for the store.
12597      * @param {Object} value the value to set as. or false on reset?
12598      */
12599     setFromData : function(o){
12600         
12601         if(this.multiple){
12602             this.addItem(o);
12603             return;
12604         }
12605             
12606         var dv = ''; // display value
12607         var vv = ''; // value value..
12608         this.lastData = o;
12609         if (this.displayField) {
12610             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12611         } else {
12612             // this is an error condition!!!
12613             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12614         }
12615         
12616         if(this.valueField){
12617             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12618         }
12619         
12620         var close = this.closeTriggerEl();
12621         
12622         if(close){
12623             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12624         }
12625         
12626         if(this.hiddenField){
12627             this.hiddenField.dom.value = vv;
12628             
12629             this.lastSelectionText = dv;
12630             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12631             this.value = vv;
12632             return;
12633         }
12634         // no hidden field.. - we store the value in 'value', but still display
12635         // display field!!!!
12636         this.lastSelectionText = dv;
12637         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12638         this.value = vv;
12639         
12640         
12641         
12642     },
12643     // private
12644     reset : function(){
12645         // overridden so that last data is reset..
12646         
12647         if(this.multiple){
12648             this.clearItem();
12649             return;
12650         }
12651         
12652         this.setValue(this.originalValue);
12653         this.clearInvalid();
12654         this.lastData = false;
12655         if (this.view) {
12656             this.view.clearSelections();
12657         }
12658     },
12659     // private
12660     findRecord : function(prop, value){
12661         var record;
12662         if(this.store.getCount() > 0){
12663             this.store.each(function(r){
12664                 if(r.data[prop] == value){
12665                     record = r;
12666                     return false;
12667                 }
12668                 return true;
12669             });
12670         }
12671         return record;
12672     },
12673     
12674     getName: function()
12675     {
12676         // returns hidden if it's set..
12677         if (!this.rendered) {return ''};
12678         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12679         
12680     },
12681     // private
12682     onViewMove : function(e, t){
12683         this.inKeyMode = false;
12684     },
12685
12686     // private
12687     onViewOver : function(e, t){
12688         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12689             return;
12690         }
12691         var item = this.view.findItemFromChild(t);
12692         
12693         if(item){
12694             var index = this.view.indexOf(item);
12695             this.select(index, false);
12696         }
12697     },
12698
12699     // private
12700     onViewClick : function(view, doFocus, el, e)
12701     {
12702         var index = this.view.getSelectedIndexes()[0];
12703         
12704         var r = this.store.getAt(index);
12705         
12706         if(this.tickable){
12707             
12708             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12709                 return;
12710             }
12711             
12712             var rm = false;
12713             var _this = this;
12714             
12715             Roo.each(this.tickItems, function(v,k){
12716                 
12717                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12718                     Roo.log(v);
12719                     _this.tickItems.splice(k, 1);
12720                     
12721                     if(typeof(e) == 'undefined' && view == false){
12722                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12723                     }
12724                     
12725                     rm = true;
12726                     return;
12727                 }
12728             });
12729             
12730             if(rm){
12731                 return;
12732             }
12733             
12734             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12735                 this.tickItems.push(r.data);
12736             }
12737             
12738             if(typeof(e) == 'undefined' && view == false){
12739                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12740             }
12741                     
12742             return;
12743         }
12744         
12745         if(r){
12746             this.onSelect(r, index);
12747         }
12748         if(doFocus !== false && !this.blockFocus){
12749             this.inputEl().focus();
12750         }
12751     },
12752
12753     // private
12754     restrictHeight : function(){
12755         //this.innerList.dom.style.height = '';
12756         //var inner = this.innerList.dom;
12757         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12758         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12759         //this.list.beginUpdate();
12760         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12761         this.list.alignTo(this.inputEl(), this.listAlign);
12762         this.list.alignTo(this.inputEl(), this.listAlign);
12763         //this.list.endUpdate();
12764     },
12765
12766     // private
12767     onEmptyResults : function(){
12768         
12769         if(this.tickable && this.editable){
12770             this.restrictHeight();
12771             return;
12772         }
12773         
12774         this.collapse();
12775     },
12776
12777     /**
12778      * Returns true if the dropdown list is expanded, else false.
12779      */
12780     isExpanded : function(){
12781         return this.list.isVisible();
12782     },
12783
12784     /**
12785      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12786      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12787      * @param {String} value The data value of the item to select
12788      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12789      * selected item if it is not currently in view (defaults to true)
12790      * @return {Boolean} True if the value matched an item in the list, else false
12791      */
12792     selectByValue : function(v, scrollIntoView){
12793         if(v !== undefined && v !== null){
12794             var r = this.findRecord(this.valueField || this.displayField, v);
12795             if(r){
12796                 this.select(this.store.indexOf(r), scrollIntoView);
12797                 return true;
12798             }
12799         }
12800         return false;
12801     },
12802
12803     /**
12804      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12805      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12806      * @param {Number} index The zero-based index of the list item to select
12807      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12808      * selected item if it is not currently in view (defaults to true)
12809      */
12810     select : function(index, scrollIntoView){
12811         this.selectedIndex = index;
12812         this.view.select(index);
12813         if(scrollIntoView !== false){
12814             var el = this.view.getNode(index);
12815             /*
12816              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12817              */
12818             if(el){
12819                 this.list.scrollChildIntoView(el, false);
12820             }
12821         }
12822     },
12823
12824     // private
12825     selectNext : function(){
12826         var ct = this.store.getCount();
12827         if(ct > 0){
12828             if(this.selectedIndex == -1){
12829                 this.select(0);
12830             }else if(this.selectedIndex < ct-1){
12831                 this.select(this.selectedIndex+1);
12832             }
12833         }
12834     },
12835
12836     // private
12837     selectPrev : function(){
12838         var ct = this.store.getCount();
12839         if(ct > 0){
12840             if(this.selectedIndex == -1){
12841                 this.select(0);
12842             }else if(this.selectedIndex != 0){
12843                 this.select(this.selectedIndex-1);
12844             }
12845         }
12846     },
12847
12848     // private
12849     onKeyUp : function(e){
12850         if(this.editable !== false && !e.isSpecialKey()){
12851             this.lastKey = e.getKey();
12852             this.dqTask.delay(this.queryDelay);
12853         }
12854     },
12855
12856     // private
12857     validateBlur : function(){
12858         return !this.list || !this.list.isVisible();   
12859     },
12860
12861     // private
12862     initQuery : function(){
12863         
12864         var v = this.getRawValue();
12865         
12866         if(this.tickable && this.editable){
12867             v = this.tickableInputEl().getValue();
12868         }
12869         
12870         this.doQuery(v);
12871     },
12872
12873     // private
12874     doForce : function(){
12875         if(this.inputEl().dom.value.length > 0){
12876             this.inputEl().dom.value =
12877                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12878              
12879         }
12880     },
12881
12882     /**
12883      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12884      * query allowing the query action to be canceled if needed.
12885      * @param {String} query The SQL query to execute
12886      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12887      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12888      * saved in the current store (defaults to false)
12889      */
12890     doQuery : function(q, forceAll){
12891         
12892         if(q === undefined || q === null){
12893             q = '';
12894         }
12895         var qe = {
12896             query: q,
12897             forceAll: forceAll,
12898             combo: this,
12899             cancel:false
12900         };
12901         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12902             return false;
12903         }
12904         q = qe.query;
12905         
12906         forceAll = qe.forceAll;
12907         if(forceAll === true || (q.length >= this.minChars)){
12908             
12909             this.hasQuery = true;
12910             
12911             if(this.lastQuery != q || this.alwaysQuery){
12912                 this.lastQuery = q;
12913                 if(this.mode == 'local'){
12914                     this.selectedIndex = -1;
12915                     if(forceAll){
12916                         this.store.clearFilter();
12917                     }else{
12918                         
12919                         if(this.specialFilter){
12920                             this.fireEvent('specialfilter', this);
12921                             this.onLoad();
12922                             return;
12923                         }
12924                         
12925                         this.store.filter(this.displayField, q);
12926                     }
12927                     
12928                     this.store.fireEvent("datachanged", this.store);
12929                     
12930                     this.onLoad();
12931                     
12932                     
12933                 }else{
12934                     
12935                     this.store.baseParams[this.queryParam] = q;
12936                     
12937                     var options = {params : this.getParams(q)};
12938                     
12939                     if(this.loadNext){
12940                         options.add = true;
12941                         options.params.start = this.page * this.pageSize;
12942                     }
12943                     
12944                     this.store.load(options);
12945                     
12946                     /*
12947                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12948                      *  we should expand the list on onLoad
12949                      *  so command out it
12950                      */
12951 //                    this.expand();
12952                 }
12953             }else{
12954                 this.selectedIndex = -1;
12955                 this.onLoad();   
12956             }
12957         }
12958         
12959         this.loadNext = false;
12960     },
12961     
12962     // private
12963     getParams : function(q){
12964         var p = {};
12965         //p[this.queryParam] = q;
12966         
12967         if(this.pageSize){
12968             p.start = 0;
12969             p.limit = this.pageSize;
12970         }
12971         return p;
12972     },
12973
12974     /**
12975      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12976      */
12977     collapse : function(){
12978         if(!this.isExpanded()){
12979             return;
12980         }
12981         
12982         this.list.hide();
12983         
12984         if(this.tickable){
12985             this.hasFocus = false;
12986             this.okBtn.hide();
12987             this.cancelBtn.hide();
12988             this.trigger.show();
12989             
12990             if(this.editable){
12991                 this.tickableInputEl().dom.value = '';
12992                 this.tickableInputEl().blur();
12993             }
12994             
12995         }
12996         
12997         Roo.get(document).un('mousedown', this.collapseIf, this);
12998         Roo.get(document).un('mousewheel', this.collapseIf, this);
12999         if (!this.editable) {
13000             Roo.get(document).un('keydown', this.listKeyPress, this);
13001         }
13002         this.fireEvent('collapse', this);
13003     },
13004
13005     // private
13006     collapseIf : function(e){
13007         var in_combo  = e.within(this.el);
13008         var in_list =  e.within(this.list);
13009         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13010         
13011         if (in_combo || in_list || is_list) {
13012             //e.stopPropagation();
13013             return;
13014         }
13015         
13016         if(this.tickable){
13017             this.onTickableFooterButtonClick(e, false, false);
13018         }
13019
13020         this.collapse();
13021         
13022     },
13023
13024     /**
13025      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13026      */
13027     expand : function(){
13028        
13029         if(this.isExpanded() || !this.hasFocus){
13030             return;
13031         }
13032         
13033         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13034         this.list.setWidth(lw);
13035         
13036         
13037          Roo.log('expand');
13038         
13039         this.list.show();
13040         
13041         this.restrictHeight();
13042         
13043         if(this.tickable){
13044             
13045             this.tickItems = Roo.apply([], this.item);
13046             
13047             this.okBtn.show();
13048             this.cancelBtn.show();
13049             this.trigger.hide();
13050             
13051             if(this.editable){
13052                 this.tickableInputEl().focus();
13053             }
13054             
13055         }
13056         
13057         Roo.get(document).on('mousedown', this.collapseIf, this);
13058         Roo.get(document).on('mousewheel', this.collapseIf, this);
13059         if (!this.editable) {
13060             Roo.get(document).on('keydown', this.listKeyPress, this);
13061         }
13062         
13063         this.fireEvent('expand', this);
13064     },
13065
13066     // private
13067     // Implements the default empty TriggerField.onTriggerClick function
13068     onTriggerClick : function(e)
13069     {
13070         Roo.log('trigger click');
13071         
13072         if(this.disabled || !this.triggerList){
13073             return;
13074         }
13075         
13076         this.page = 0;
13077         this.loadNext = false;
13078         
13079         if(this.isExpanded()){
13080             this.collapse();
13081             if (!this.blockFocus) {
13082                 this.inputEl().focus();
13083             }
13084             
13085         }else {
13086             this.hasFocus = true;
13087             if(this.triggerAction == 'all') {
13088                 this.doQuery(this.allQuery, true);
13089             } else {
13090                 this.doQuery(this.getRawValue());
13091             }
13092             if (!this.blockFocus) {
13093                 this.inputEl().focus();
13094             }
13095         }
13096     },
13097     
13098     onTickableTriggerClick : function(e)
13099     {
13100         if(this.disabled){
13101             return;
13102         }
13103         
13104         this.page = 0;
13105         this.loadNext = false;
13106         this.hasFocus = true;
13107         
13108         if(this.triggerAction == 'all') {
13109             this.doQuery(this.allQuery, true);
13110         } else {
13111             this.doQuery(this.getRawValue());
13112         }
13113     },
13114     
13115     onSearchFieldClick : function(e)
13116     {
13117         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13118             this.onTickableFooterButtonClick(e, false, false);
13119             return;
13120         }
13121         
13122         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13123             return;
13124         }
13125         
13126         this.page = 0;
13127         this.loadNext = false;
13128         this.hasFocus = true;
13129         
13130         if(this.triggerAction == 'all') {
13131             this.doQuery(this.allQuery, true);
13132         } else {
13133             this.doQuery(this.getRawValue());
13134         }
13135     },
13136     
13137     listKeyPress : function(e)
13138     {
13139         //Roo.log('listkeypress');
13140         // scroll to first matching element based on key pres..
13141         if (e.isSpecialKey()) {
13142             return false;
13143         }
13144         var k = String.fromCharCode(e.getKey()).toUpperCase();
13145         //Roo.log(k);
13146         var match  = false;
13147         var csel = this.view.getSelectedNodes();
13148         var cselitem = false;
13149         if (csel.length) {
13150             var ix = this.view.indexOf(csel[0]);
13151             cselitem  = this.store.getAt(ix);
13152             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13153                 cselitem = false;
13154             }
13155             
13156         }
13157         
13158         this.store.each(function(v) { 
13159             if (cselitem) {
13160                 // start at existing selection.
13161                 if (cselitem.id == v.id) {
13162                     cselitem = false;
13163                 }
13164                 return true;
13165             }
13166                 
13167             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13168                 match = this.store.indexOf(v);
13169                 return false;
13170             }
13171             return true;
13172         }, this);
13173         
13174         if (match === false) {
13175             return true; // no more action?
13176         }
13177         // scroll to?
13178         this.view.select(match);
13179         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13180         sn.scrollIntoView(sn.dom.parentNode, false);
13181     },
13182     
13183     onViewScroll : function(e, t){
13184         
13185         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
13186             return;
13187         }
13188         
13189         this.hasQuery = true;
13190         
13191         this.loading = this.list.select('.loading', true).first();
13192         
13193         if(this.loading === null){
13194             this.list.createChild({
13195                 tag: 'div',
13196                 cls: 'loading select2-more-results select2-active',
13197                 html: 'Loading more results...'
13198             });
13199             
13200             this.loading = this.list.select('.loading', true).first();
13201             
13202             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13203             
13204             this.loading.hide();
13205         }
13206         
13207         this.loading.show();
13208         
13209         var _combo = this;
13210         
13211         this.page++;
13212         this.loadNext = true;
13213         
13214         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13215         
13216         return;
13217     },
13218     
13219     addItem : function(o)
13220     {   
13221         var dv = ''; // display value
13222         
13223         if (this.displayField) {
13224             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13225         } else {
13226             // this is an error condition!!!
13227             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13228         }
13229         
13230         if(!dv.length){
13231             return;
13232         }
13233         
13234         var choice = this.choices.createChild({
13235             tag: 'li',
13236             cls: 'select2-search-choice',
13237             cn: [
13238                 {
13239                     tag: 'div',
13240                     html: dv
13241                 },
13242                 {
13243                     tag: 'a',
13244                     href: '#',
13245                     cls: 'select2-search-choice-close',
13246                     tabindex: '-1'
13247                 }
13248             ]
13249             
13250         }, this.searchField);
13251         
13252         var close = choice.select('a.select2-search-choice-close', true).first();
13253         
13254         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13255         
13256         this.item.push(o);
13257         
13258         this.lastData = o;
13259         
13260         this.syncValue();
13261         
13262         this.inputEl().dom.value = '';
13263         
13264         this.validate();
13265     },
13266     
13267     onRemoveItem : function(e, _self, o)
13268     {
13269         e.preventDefault();
13270         
13271         this.lastItem = Roo.apply([], this.item);
13272         
13273         var index = this.item.indexOf(o.data) * 1;
13274         
13275         if( index < 0){
13276             Roo.log('not this item?!');
13277             return;
13278         }
13279         
13280         this.item.splice(index, 1);
13281         o.item.remove();
13282         
13283         this.syncValue();
13284         
13285         this.fireEvent('remove', this, e);
13286         
13287         this.validate();
13288         
13289     },
13290     
13291     syncValue : function()
13292     {
13293         if(!this.item.length){
13294             this.clearValue();
13295             return;
13296         }
13297             
13298         var value = [];
13299         var _this = this;
13300         Roo.each(this.item, function(i){
13301             if(_this.valueField){
13302                 value.push(i[_this.valueField]);
13303                 return;
13304             }
13305
13306             value.push(i);
13307         });
13308
13309         this.value = value.join(',');
13310
13311         if(this.hiddenField){
13312             this.hiddenField.dom.value = this.value;
13313         }
13314         
13315         this.store.fireEvent("datachanged", this.store);
13316     },
13317     
13318     clearItem : function()
13319     {
13320         if(!this.multiple){
13321             return;
13322         }
13323         
13324         this.item = [];
13325         
13326         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13327            c.remove();
13328         });
13329         
13330         this.syncValue();
13331         
13332         this.validate();
13333         
13334         if(this.tickable && !Roo.isTouch){
13335             this.view.refresh();
13336         }
13337     },
13338     
13339     inputEl: function ()
13340     {
13341         if(Roo.isTouch && this.mobileTouchView){
13342             return this.el.select('input.form-control',true).first();
13343         }
13344         
13345         if(this.tickable){
13346             return this.searchField;
13347         }
13348         
13349         return this.el.select('input.form-control',true).first();
13350     },
13351     
13352     
13353     onTickableFooterButtonClick : function(e, btn, el)
13354     {
13355         e.preventDefault();
13356         
13357         this.lastItem = Roo.apply([], this.item);
13358         
13359         if(btn && btn.name == 'cancel'){
13360             this.tickItems = Roo.apply([], this.item);
13361             this.collapse();
13362             return;
13363         }
13364         
13365         this.clearItem();
13366         
13367         var _this = this;
13368         
13369         Roo.each(this.tickItems, function(o){
13370             _this.addItem(o);
13371         });
13372         
13373         this.collapse();
13374         
13375     },
13376     
13377     validate : function()
13378     {
13379         var v = this.getRawValue();
13380         
13381         if(this.multiple){
13382             v = this.getValue();
13383         }
13384         
13385         if(this.disabled || this.allowBlank || v.length){
13386             this.markValid();
13387             return true;
13388         }
13389         
13390         this.markInvalid();
13391         return false;
13392     },
13393     
13394     tickableInputEl : function()
13395     {
13396         if(!this.tickable || !this.editable){
13397             return this.inputEl();
13398         }
13399         
13400         return this.inputEl().select('.select2-search-field-input', true).first();
13401     },
13402     
13403     
13404     getAutoCreateTouchView : function()
13405     {
13406         var id = Roo.id();
13407         
13408         var cfg = {
13409             cls: 'form-group' //input-group
13410         };
13411         
13412         var input =  {
13413             tag: 'input',
13414             id : id,
13415             type : this.inputType,
13416             cls : 'form-control x-combo-noedit',
13417             autocomplete: 'new-password',
13418             placeholder : this.placeholder || '',
13419             readonly : true
13420         };
13421         
13422         if (this.name) {
13423             input.name = this.name;
13424         }
13425         
13426         if (this.size) {
13427             input.cls += ' input-' + this.size;
13428         }
13429         
13430         if (this.disabled) {
13431             input.disabled = true;
13432         }
13433         
13434         var inputblock = {
13435             cls : '',
13436             cn : [
13437                 input
13438             ]
13439         };
13440         
13441         if(this.before){
13442             inputblock.cls += ' input-group';
13443             
13444             inputblock.cn.unshift({
13445                 tag :'span',
13446                 cls : 'input-group-addon',
13447                 html : this.before
13448             });
13449         }
13450         
13451         if(this.removable && !this.multiple){
13452             inputblock.cls += ' roo-removable';
13453             
13454             inputblock.cn.push({
13455                 tag: 'button',
13456                 html : 'x',
13457                 cls : 'roo-combo-removable-btn close'
13458             });
13459         }
13460
13461         if(this.hasFeedback && !this.allowBlank){
13462             
13463             inputblock.cls += ' has-feedback';
13464             
13465             inputblock.cn.push({
13466                 tag: 'span',
13467                 cls: 'glyphicon form-control-feedback'
13468             });
13469             
13470         }
13471         
13472         if (this.after) {
13473             
13474             inputblock.cls += (this.before) ? '' : ' input-group';
13475             
13476             inputblock.cn.push({
13477                 tag :'span',
13478                 cls : 'input-group-addon',
13479                 html : this.after
13480             });
13481         }
13482
13483         var box = {
13484             tag: 'div',
13485             cn: [
13486                 {
13487                     tag: 'input',
13488                     type : 'hidden',
13489                     cls: 'form-hidden-field'
13490                 },
13491                 inputblock
13492             ]
13493             
13494         };
13495         
13496         if(this.multiple){
13497             box = {
13498                 tag: 'div',
13499                 cn: [
13500                     {
13501                         tag: 'input',
13502                         type : 'hidden',
13503                         cls: 'form-hidden-field'
13504                     },
13505                     {
13506                         tag: 'ul',
13507                         cls: 'select2-choices',
13508                         cn:[
13509                             {
13510                                 tag: 'li',
13511                                 cls: 'select2-search-field',
13512                                 cn: [
13513
13514                                     inputblock
13515                                 ]
13516                             }
13517                         ]
13518                     }
13519                 ]
13520             }
13521         };
13522         
13523         var combobox = {
13524             cls: 'select2-container input-group',
13525             cn: [
13526                 box
13527             ]
13528         };
13529         
13530         if(this.multiple){
13531             combobox.cls += ' select2-container-multi';
13532         }
13533         
13534         var align = this.labelAlign || this.parentLabelAlign();
13535         
13536         cfg.cn = combobox;
13537         
13538         if(this.fieldLabel.length){
13539             
13540             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13541             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13542             
13543             cfg.cn = [
13544                 {
13545                     tag: 'label',
13546                     cls : 'control-label ' + lw,
13547                     html : this.fieldLabel
13548
13549                 },
13550                 {
13551                     cls : cw, 
13552                     cn: [
13553                         combobox
13554                     ]
13555                 }
13556             ];
13557         }
13558         
13559         var settings = this;
13560         
13561         ['xs','sm','md','lg'].map(function(size){
13562             if (settings[size]) {
13563                 cfg.cls += ' col-' + size + '-' + settings[size];
13564             }
13565         });
13566         
13567         return cfg;
13568     },
13569     
13570     initTouchView : function()
13571     {
13572         this.renderTouchView();
13573         
13574         this.touchViewEl.on('scroll', function(){
13575             this.el.dom.scrollTop = 0;
13576         }, this);
13577         
13578         this.originalValue = this.getValue();
13579         
13580         this.inputEl().on("click", this.showTouchView, this);
13581         
13582         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13583         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13584         
13585         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13586         
13587         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13588         this.store.on('load', this.onTouchViewLoad, this);
13589         this.store.on('loadexception', this.onTouchViewLoadException, this);
13590         
13591         if(this.hiddenName){
13592             
13593             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13594             
13595             this.hiddenField.dom.value =
13596                 this.hiddenValue !== undefined ? this.hiddenValue :
13597                 this.value !== undefined ? this.value : '';
13598         
13599             this.el.dom.removeAttribute('name');
13600             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13601         }
13602         
13603         if(this.multiple){
13604             this.choices = this.el.select('ul.select2-choices', true).first();
13605             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13606         }
13607         
13608         if(this.removable && !this.multiple){
13609             var close = this.closeTriggerEl();
13610             if(close){
13611                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13612                 close.on('click', this.removeBtnClick, this, close);
13613             }
13614         }
13615         /*
13616          * fix the bug in Safari iOS8
13617          */
13618         this.inputEl().on("focus", function(e){
13619             document.activeElement.blur();
13620         }, this);
13621         
13622         return;
13623         
13624         
13625     },
13626     
13627     renderTouchView : function()
13628     {
13629         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13630         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13631         
13632         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13633         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13634         
13635         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13636         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13637         this.touchViewBodyEl.setStyle('overflow', 'auto');
13638         
13639         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13640         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13641         
13642         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13643         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13644         
13645     },
13646     
13647     showTouchView : function()
13648     {
13649         if(this.disabled){
13650             return;
13651         }
13652         
13653         this.touchViewHeaderEl.hide();
13654
13655         if(this.fieldLabel.length){
13656             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13657             this.touchViewHeaderEl.show();
13658         }
13659
13660         this.touchViewEl.show();
13661
13662         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13663         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13664
13665         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13666
13667         if(this.fieldLabel.length){
13668             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13669         }
13670         
13671         this.touchViewBodyEl.setHeight(bodyHeight);
13672
13673         if(this.animate){
13674             var _this = this;
13675             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13676         }else{
13677             this.touchViewEl.addClass('in');
13678         }
13679
13680         this.doTouchViewQuery();
13681         
13682     },
13683     
13684     hideTouchView : function()
13685     {
13686         this.touchViewEl.removeClass('in');
13687
13688         if(this.animate){
13689             var _this = this;
13690             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13691         }else{
13692             this.touchViewEl.setStyle('display', 'none');
13693         }
13694         
13695     },
13696     
13697     setTouchViewValue : function()
13698     {
13699         if(this.multiple){
13700             this.clearItem();
13701         
13702             var _this = this;
13703
13704             Roo.each(this.tickItems, function(o){
13705                 this.addItem(o);
13706             }, this);
13707         }
13708         
13709         this.hideTouchView();
13710     },
13711     
13712     doTouchViewQuery : function()
13713     {
13714         var qe = {
13715             query: '',
13716             forceAll: true,
13717             combo: this,
13718             cancel:false
13719         };
13720         
13721         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13722             return false;
13723         }
13724         
13725         if(!this.alwaysQuery || this.mode == 'local'){
13726             this.onTouchViewLoad();
13727             return;
13728         }
13729         
13730         this.store.load();
13731     },
13732     
13733     onTouchViewBeforeLoad : function(combo,opts)
13734     {
13735         return;
13736     },
13737
13738     // private
13739     onTouchViewLoad : function()
13740     {
13741         if(this.store.getCount() < 1){
13742             this.onTouchViewEmptyResults();
13743             return;
13744         }
13745         
13746         this.clearTouchView();
13747         
13748         var rawValue = this.getRawValue();
13749         
13750         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13751         
13752         this.tickItems = [];
13753         
13754         this.store.data.each(function(d, rowIndex){
13755             var row = this.touchViewListGroup.createChild(template);
13756             
13757             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13758                 var cfg = {
13759                     data : d.data,
13760                     html : d.data[this.displayField]
13761                 };
13762                 
13763                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13764                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13765                 }
13766             }
13767             
13768             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13769                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13770             }
13771             
13772             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13773                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13774                 this.tickItems.push(d.data);
13775             }
13776             
13777             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13778             
13779         }, this);
13780         
13781         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13782         
13783         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13784
13785         if(this.fieldLabel.length){
13786             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13787         }
13788
13789         var listHeight = this.touchViewListGroup.getHeight();
13790         
13791         var _this = this;
13792         
13793         if(firstChecked && listHeight > bodyHeight){
13794             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13795         }
13796         
13797     },
13798     
13799     onTouchViewLoadException : function()
13800     {
13801         this.hideTouchView();
13802     },
13803     
13804     onTouchViewEmptyResults : function()
13805     {
13806         this.clearTouchView();
13807         
13808         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13809         
13810         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13811         
13812     },
13813     
13814     clearTouchView : function()
13815     {
13816         this.touchViewListGroup.dom.innerHTML = '';
13817     },
13818     
13819     onTouchViewClick : function(e, el, o)
13820     {
13821         e.preventDefault();
13822         
13823         var row = o.row;
13824         var rowIndex = o.rowIndex;
13825         
13826         var r = this.store.getAt(rowIndex);
13827         
13828         if(!this.multiple){
13829             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13830                 c.dom.removeAttribute('checked');
13831             }, this);
13832             
13833             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13834         
13835             this.setFromData(r.data);
13836             
13837             var close = this.closeTriggerEl();
13838         
13839             if(close){
13840                 close.show();
13841             }
13842
13843             this.hideTouchView();
13844             
13845             this.fireEvent('select', this, r, rowIndex);
13846             
13847             return;
13848         }
13849         
13850         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13851             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13852             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13853             return;
13854         }
13855         
13856         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13857         this.addItem(r.data);
13858         this.tickItems.push(r.data);
13859         
13860     }
13861     
13862
13863     /** 
13864     * @cfg {Boolean} grow 
13865     * @hide 
13866     */
13867     /** 
13868     * @cfg {Number} growMin 
13869     * @hide 
13870     */
13871     /** 
13872     * @cfg {Number} growMax 
13873     * @hide 
13874     */
13875     /**
13876      * @hide
13877      * @method autoSize
13878      */
13879 });
13880
13881 Roo.apply(Roo.bootstrap.ComboBox,  {
13882     
13883     header : {
13884         tag: 'div',
13885         cls: 'modal-header',
13886         cn: [
13887             {
13888                 tag: 'h4',
13889                 cls: 'modal-title'
13890             }
13891         ]
13892     },
13893     
13894     body : {
13895         tag: 'div',
13896         cls: 'modal-body',
13897         cn: [
13898             {
13899                 tag: 'ul',
13900                 cls: 'list-group'
13901             }
13902         ]
13903     },
13904     
13905     listItemRadio : {
13906         tag: 'li',
13907         cls: 'list-group-item',
13908         cn: [
13909             {
13910                 tag: 'span',
13911                 cls: 'roo-combobox-list-group-item-value'
13912             },
13913             {
13914                 tag: 'div',
13915                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13916                 cn: [
13917                     {
13918                         tag: 'input',
13919                         type: 'radio'
13920                     },
13921                     {
13922                         tag: 'label'
13923                     }
13924                 ]
13925             }
13926         ]
13927     },
13928     
13929     listItemCheckbox : {
13930         tag: 'li',
13931         cls: 'list-group-item',
13932         cn: [
13933             {
13934                 tag: 'span',
13935                 cls: 'roo-combobox-list-group-item-value'
13936             },
13937             {
13938                 tag: 'div',
13939                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13940                 cn: [
13941                     {
13942                         tag: 'input',
13943                         type: 'checkbox'
13944                     },
13945                     {
13946                         tag: 'label'
13947                     }
13948                 ]
13949             }
13950         ]
13951     },
13952     
13953     emptyResult : {
13954         tag: 'div',
13955         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13956     },
13957     
13958     footer : {
13959         tag: 'div',
13960         cls: 'modal-footer',
13961         cn: [
13962             {
13963                 tag: 'div',
13964                 cls: 'row',
13965                 cn: [
13966                     {
13967                         tag: 'div',
13968                         cls: 'col-xs-6 text-left',
13969                         cn: {
13970                             tag: 'button',
13971                             cls: 'btn btn-danger roo-touch-view-cancel',
13972                             html: 'Cancel'
13973                         }
13974                     },
13975                     {
13976                         tag: 'div',
13977                         cls: 'col-xs-6 text-right',
13978                         cn: {
13979                             tag: 'button',
13980                             cls: 'btn btn-success roo-touch-view-ok',
13981                             html: 'OK'
13982                         }
13983                     }
13984                 ]
13985             }
13986         ]
13987         
13988     }
13989 });
13990
13991 Roo.apply(Roo.bootstrap.ComboBox,  {
13992     
13993     touchViewTemplate : {
13994         tag: 'div',
13995         cls: 'modal fade roo-combobox-touch-view',
13996         cn: [
13997             {
13998                 tag: 'div',
13999                 cls: 'modal-dialog',
14000                 style : 'position:fixed', // we have to fix position....
14001                 cn: [
14002                     {
14003                         tag: 'div',
14004                         cls: 'modal-content',
14005                         cn: [
14006                             Roo.bootstrap.ComboBox.header,
14007                             Roo.bootstrap.ComboBox.body,
14008                             Roo.bootstrap.ComboBox.footer
14009                         ]
14010                     }
14011                 ]
14012             }
14013         ]
14014     }
14015 });/*
14016  * Based on:
14017  * Ext JS Library 1.1.1
14018  * Copyright(c) 2006-2007, Ext JS, LLC.
14019  *
14020  * Originally Released Under LGPL - original licence link has changed is not relivant.
14021  *
14022  * Fork - LGPL
14023  * <script type="text/javascript">
14024  */
14025
14026 /**
14027  * @class Roo.View
14028  * @extends Roo.util.Observable
14029  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14030  * This class also supports single and multi selection modes. <br>
14031  * Create a data model bound view:
14032  <pre><code>
14033  var store = new Roo.data.Store(...);
14034
14035  var view = new Roo.View({
14036     el : "my-element",
14037     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14038  
14039     singleSelect: true,
14040     selectedClass: "ydataview-selected",
14041     store: store
14042  });
14043
14044  // listen for node click?
14045  view.on("click", function(vw, index, node, e){
14046  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14047  });
14048
14049  // load XML data
14050  dataModel.load("foobar.xml");
14051  </code></pre>
14052  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14053  * <br><br>
14054  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14055  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14056  * 
14057  * Note: old style constructor is still suported (container, template, config)
14058  * 
14059  * @constructor
14060  * Create a new View
14061  * @param {Object} config The config object
14062  * 
14063  */
14064 Roo.View = function(config, depreciated_tpl, depreciated_config){
14065     
14066     this.parent = false;
14067     
14068     if (typeof(depreciated_tpl) == 'undefined') {
14069         // new way.. - universal constructor.
14070         Roo.apply(this, config);
14071         this.el  = Roo.get(this.el);
14072     } else {
14073         // old format..
14074         this.el  = Roo.get(config);
14075         this.tpl = depreciated_tpl;
14076         Roo.apply(this, depreciated_config);
14077     }
14078     this.wrapEl  = this.el.wrap().wrap();
14079     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14080     
14081     
14082     if(typeof(this.tpl) == "string"){
14083         this.tpl = new Roo.Template(this.tpl);
14084     } else {
14085         // support xtype ctors..
14086         this.tpl = new Roo.factory(this.tpl, Roo);
14087     }
14088     
14089     
14090     this.tpl.compile();
14091     
14092     /** @private */
14093     this.addEvents({
14094         /**
14095          * @event beforeclick
14096          * Fires before a click is processed. Returns false to cancel the default action.
14097          * @param {Roo.View} this
14098          * @param {Number} index The index of the target node
14099          * @param {HTMLElement} node The target node
14100          * @param {Roo.EventObject} e The raw event object
14101          */
14102             "beforeclick" : true,
14103         /**
14104          * @event click
14105          * Fires when a template node is clicked.
14106          * @param {Roo.View} this
14107          * @param {Number} index The index of the target node
14108          * @param {HTMLElement} node The target node
14109          * @param {Roo.EventObject} e The raw event object
14110          */
14111             "click" : true,
14112         /**
14113          * @event dblclick
14114          * Fires when a template node is double clicked.
14115          * @param {Roo.View} this
14116          * @param {Number} index The index of the target node
14117          * @param {HTMLElement} node The target node
14118          * @param {Roo.EventObject} e The raw event object
14119          */
14120             "dblclick" : true,
14121         /**
14122          * @event contextmenu
14123          * Fires when a template node is right clicked.
14124          * @param {Roo.View} this
14125          * @param {Number} index The index of the target node
14126          * @param {HTMLElement} node The target node
14127          * @param {Roo.EventObject} e The raw event object
14128          */
14129             "contextmenu" : true,
14130         /**
14131          * @event selectionchange
14132          * Fires when the selected nodes change.
14133          * @param {Roo.View} this
14134          * @param {Array} selections Array of the selected nodes
14135          */
14136             "selectionchange" : true,
14137     
14138         /**
14139          * @event beforeselect
14140          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14141          * @param {Roo.View} this
14142          * @param {HTMLElement} node The node to be selected
14143          * @param {Array} selections Array of currently selected nodes
14144          */
14145             "beforeselect" : true,
14146         /**
14147          * @event preparedata
14148          * Fires on every row to render, to allow you to change the data.
14149          * @param {Roo.View} this
14150          * @param {Object} data to be rendered (change this)
14151          */
14152           "preparedata" : true
14153           
14154           
14155         });
14156
14157
14158
14159     this.el.on({
14160         "click": this.onClick,
14161         "dblclick": this.onDblClick,
14162         "contextmenu": this.onContextMenu,
14163         scope:this
14164     });
14165
14166     this.selections = [];
14167     this.nodes = [];
14168     this.cmp = new Roo.CompositeElementLite([]);
14169     if(this.store){
14170         this.store = Roo.factory(this.store, Roo.data);
14171         this.setStore(this.store, true);
14172     }
14173     
14174     if ( this.footer && this.footer.xtype) {
14175            
14176          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14177         
14178         this.footer.dataSource = this.store;
14179         this.footer.container = fctr;
14180         this.footer = Roo.factory(this.footer, Roo);
14181         fctr.insertFirst(this.el);
14182         
14183         // this is a bit insane - as the paging toolbar seems to detach the el..
14184 //        dom.parentNode.parentNode.parentNode
14185          // they get detached?
14186     }
14187     
14188     
14189     Roo.View.superclass.constructor.call(this);
14190     
14191     
14192 };
14193
14194 Roo.extend(Roo.View, Roo.util.Observable, {
14195     
14196      /**
14197      * @cfg {Roo.data.Store} store Data store to load data from.
14198      */
14199     store : false,
14200     
14201     /**
14202      * @cfg {String|Roo.Element} el The container element.
14203      */
14204     el : '',
14205     
14206     /**
14207      * @cfg {String|Roo.Template} tpl The template used by this View 
14208      */
14209     tpl : false,
14210     /**
14211      * @cfg {String} dataName the named area of the template to use as the data area
14212      *                          Works with domtemplates roo-name="name"
14213      */
14214     dataName: false,
14215     /**
14216      * @cfg {String} selectedClass The css class to add to selected nodes
14217      */
14218     selectedClass : "x-view-selected",
14219      /**
14220      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14221      */
14222     emptyText : "",
14223     
14224     /**
14225      * @cfg {String} text to display on mask (default Loading)
14226      */
14227     mask : false,
14228     /**
14229      * @cfg {Boolean} multiSelect Allow multiple selection
14230      */
14231     multiSelect : false,
14232     /**
14233      * @cfg {Boolean} singleSelect Allow single selection
14234      */
14235     singleSelect:  false,
14236     
14237     /**
14238      * @cfg {Boolean} toggleSelect - selecting 
14239      */
14240     toggleSelect : false,
14241     
14242     /**
14243      * @cfg {Boolean} tickable - selecting 
14244      */
14245     tickable : false,
14246     
14247     /**
14248      * Returns the element this view is bound to.
14249      * @return {Roo.Element}
14250      */
14251     getEl : function(){
14252         return this.wrapEl;
14253     },
14254     
14255     
14256
14257     /**
14258      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14259      */
14260     refresh : function(){
14261         //Roo.log('refresh');
14262         var t = this.tpl;
14263         
14264         // if we are using something like 'domtemplate', then
14265         // the what gets used is:
14266         // t.applySubtemplate(NAME, data, wrapping data..)
14267         // the outer template then get' applied with
14268         //     the store 'extra data'
14269         // and the body get's added to the
14270         //      roo-name="data" node?
14271         //      <span class='roo-tpl-{name}'></span> ?????
14272         
14273         
14274         
14275         this.clearSelections();
14276         this.el.update("");
14277         var html = [];
14278         var records = this.store.getRange();
14279         if(records.length < 1) {
14280             
14281             // is this valid??  = should it render a template??
14282             
14283             this.el.update(this.emptyText);
14284             return;
14285         }
14286         var el = this.el;
14287         if (this.dataName) {
14288             this.el.update(t.apply(this.store.meta)); //????
14289             el = this.el.child('.roo-tpl-' + this.dataName);
14290         }
14291         
14292         for(var i = 0, len = records.length; i < len; i++){
14293             var data = this.prepareData(records[i].data, i, records[i]);
14294             this.fireEvent("preparedata", this, data, i, records[i]);
14295             
14296             var d = Roo.apply({}, data);
14297             
14298             if(this.tickable){
14299                 Roo.apply(d, {'roo-id' : Roo.id()});
14300                 
14301                 var _this = this;
14302             
14303                 Roo.each(this.parent.item, function(item){
14304                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14305                         return;
14306                     }
14307                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14308                 });
14309             }
14310             
14311             html[html.length] = Roo.util.Format.trim(
14312                 this.dataName ?
14313                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14314                     t.apply(d)
14315             );
14316         }
14317         
14318         
14319         
14320         el.update(html.join(""));
14321         this.nodes = el.dom.childNodes;
14322         this.updateIndexes(0);
14323     },
14324     
14325
14326     /**
14327      * Function to override to reformat the data that is sent to
14328      * the template for each node.
14329      * DEPRICATED - use the preparedata event handler.
14330      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14331      * a JSON object for an UpdateManager bound view).
14332      */
14333     prepareData : function(data, index, record)
14334     {
14335         this.fireEvent("preparedata", this, data, index, record);
14336         return data;
14337     },
14338
14339     onUpdate : function(ds, record){
14340         // Roo.log('on update');   
14341         this.clearSelections();
14342         var index = this.store.indexOf(record);
14343         var n = this.nodes[index];
14344         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14345         n.parentNode.removeChild(n);
14346         this.updateIndexes(index, index);
14347     },
14348
14349     
14350     
14351 // --------- FIXME     
14352     onAdd : function(ds, records, index)
14353     {
14354         //Roo.log(['on Add', ds, records, index] );        
14355         this.clearSelections();
14356         if(this.nodes.length == 0){
14357             this.refresh();
14358             return;
14359         }
14360         var n = this.nodes[index];
14361         for(var i = 0, len = records.length; i < len; i++){
14362             var d = this.prepareData(records[i].data, i, records[i]);
14363             if(n){
14364                 this.tpl.insertBefore(n, d);
14365             }else{
14366                 
14367                 this.tpl.append(this.el, d);
14368             }
14369         }
14370         this.updateIndexes(index);
14371     },
14372
14373     onRemove : function(ds, record, index){
14374        // Roo.log('onRemove');
14375         this.clearSelections();
14376         var el = this.dataName  ?
14377             this.el.child('.roo-tpl-' + this.dataName) :
14378             this.el; 
14379         
14380         el.dom.removeChild(this.nodes[index]);
14381         this.updateIndexes(index);
14382     },
14383
14384     /**
14385      * Refresh an individual node.
14386      * @param {Number} index
14387      */
14388     refreshNode : function(index){
14389         this.onUpdate(this.store, this.store.getAt(index));
14390     },
14391
14392     updateIndexes : function(startIndex, endIndex){
14393         var ns = this.nodes;
14394         startIndex = startIndex || 0;
14395         endIndex = endIndex || ns.length - 1;
14396         for(var i = startIndex; i <= endIndex; i++){
14397             ns[i].nodeIndex = i;
14398         }
14399     },
14400
14401     /**
14402      * Changes the data store this view uses and refresh the view.
14403      * @param {Store} store
14404      */
14405     setStore : function(store, initial){
14406         if(!initial && this.store){
14407             this.store.un("datachanged", this.refresh);
14408             this.store.un("add", this.onAdd);
14409             this.store.un("remove", this.onRemove);
14410             this.store.un("update", this.onUpdate);
14411             this.store.un("clear", this.refresh);
14412             this.store.un("beforeload", this.onBeforeLoad);
14413             this.store.un("load", this.onLoad);
14414             this.store.un("loadexception", this.onLoad);
14415         }
14416         if(store){
14417           
14418             store.on("datachanged", this.refresh, this);
14419             store.on("add", this.onAdd, this);
14420             store.on("remove", this.onRemove, this);
14421             store.on("update", this.onUpdate, this);
14422             store.on("clear", this.refresh, this);
14423             store.on("beforeload", this.onBeforeLoad, this);
14424             store.on("load", this.onLoad, this);
14425             store.on("loadexception", this.onLoad, this);
14426         }
14427         
14428         if(store){
14429             this.refresh();
14430         }
14431     },
14432     /**
14433      * onbeforeLoad - masks the loading area.
14434      *
14435      */
14436     onBeforeLoad : function(store,opts)
14437     {
14438          //Roo.log('onBeforeLoad');   
14439         if (!opts.add) {
14440             this.el.update("");
14441         }
14442         this.el.mask(this.mask ? this.mask : "Loading" ); 
14443     },
14444     onLoad : function ()
14445     {
14446         this.el.unmask();
14447     },
14448     
14449
14450     /**
14451      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14452      * @param {HTMLElement} node
14453      * @return {HTMLElement} The template node
14454      */
14455     findItemFromChild : function(node){
14456         var el = this.dataName  ?
14457             this.el.child('.roo-tpl-' + this.dataName,true) :
14458             this.el.dom; 
14459         
14460         if(!node || node.parentNode == el){
14461                     return node;
14462             }
14463             var p = node.parentNode;
14464             while(p && p != el){
14465             if(p.parentNode == el){
14466                 return p;
14467             }
14468             p = p.parentNode;
14469         }
14470             return null;
14471     },
14472
14473     /** @ignore */
14474     onClick : function(e){
14475         var item = this.findItemFromChild(e.getTarget());
14476         if(item){
14477             var index = this.indexOf(item);
14478             if(this.onItemClick(item, index, e) !== false){
14479                 this.fireEvent("click", this, index, item, e);
14480             }
14481         }else{
14482             this.clearSelections();
14483         }
14484     },
14485
14486     /** @ignore */
14487     onContextMenu : function(e){
14488         var item = this.findItemFromChild(e.getTarget());
14489         if(item){
14490             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14491         }
14492     },
14493
14494     /** @ignore */
14495     onDblClick : function(e){
14496         var item = this.findItemFromChild(e.getTarget());
14497         if(item){
14498             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14499         }
14500     },
14501
14502     onItemClick : function(item, index, e)
14503     {
14504         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14505             return false;
14506         }
14507         if (this.toggleSelect) {
14508             var m = this.isSelected(item) ? 'unselect' : 'select';
14509             //Roo.log(m);
14510             var _t = this;
14511             _t[m](item, true, false);
14512             return true;
14513         }
14514         if(this.multiSelect || this.singleSelect){
14515             if(this.multiSelect && e.shiftKey && this.lastSelection){
14516                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14517             }else{
14518                 this.select(item, this.multiSelect && e.ctrlKey);
14519                 this.lastSelection = item;
14520             }
14521             
14522             if(!this.tickable){
14523                 e.preventDefault();
14524             }
14525             
14526         }
14527         return true;
14528     },
14529
14530     /**
14531      * Get the number of selected nodes.
14532      * @return {Number}
14533      */
14534     getSelectionCount : function(){
14535         return this.selections.length;
14536     },
14537
14538     /**
14539      * Get the currently selected nodes.
14540      * @return {Array} An array of HTMLElements
14541      */
14542     getSelectedNodes : function(){
14543         return this.selections;
14544     },
14545
14546     /**
14547      * Get the indexes of the selected nodes.
14548      * @return {Array}
14549      */
14550     getSelectedIndexes : function(){
14551         var indexes = [], s = this.selections;
14552         for(var i = 0, len = s.length; i < len; i++){
14553             indexes.push(s[i].nodeIndex);
14554         }
14555         return indexes;
14556     },
14557
14558     /**
14559      * Clear all selections
14560      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14561      */
14562     clearSelections : function(suppressEvent){
14563         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14564             this.cmp.elements = this.selections;
14565             this.cmp.removeClass(this.selectedClass);
14566             this.selections = [];
14567             if(!suppressEvent){
14568                 this.fireEvent("selectionchange", this, this.selections);
14569             }
14570         }
14571     },
14572
14573     /**
14574      * Returns true if the passed node is selected
14575      * @param {HTMLElement/Number} node The node or node index
14576      * @return {Boolean}
14577      */
14578     isSelected : function(node){
14579         var s = this.selections;
14580         if(s.length < 1){
14581             return false;
14582         }
14583         node = this.getNode(node);
14584         return s.indexOf(node) !== -1;
14585     },
14586
14587     /**
14588      * Selects nodes.
14589      * @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
14590      * @param {Boolean} keepExisting (optional) true to keep existing selections
14591      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14592      */
14593     select : function(nodeInfo, keepExisting, suppressEvent){
14594         if(nodeInfo instanceof Array){
14595             if(!keepExisting){
14596                 this.clearSelections(true);
14597             }
14598             for(var i = 0, len = nodeInfo.length; i < len; i++){
14599                 this.select(nodeInfo[i], true, true);
14600             }
14601             return;
14602         } 
14603         var node = this.getNode(nodeInfo);
14604         if(!node || this.isSelected(node)){
14605             return; // already selected.
14606         }
14607         if(!keepExisting){
14608             this.clearSelections(true);
14609         }
14610         
14611         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14612             Roo.fly(node).addClass(this.selectedClass);
14613             this.selections.push(node);
14614             if(!suppressEvent){
14615                 this.fireEvent("selectionchange", this, this.selections);
14616             }
14617         }
14618         
14619         
14620     },
14621       /**
14622      * Unselects nodes.
14623      * @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
14624      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14625      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14626      */
14627     unselect : function(nodeInfo, keepExisting, suppressEvent)
14628     {
14629         if(nodeInfo instanceof Array){
14630             Roo.each(this.selections, function(s) {
14631                 this.unselect(s, nodeInfo);
14632             }, this);
14633             return;
14634         }
14635         var node = this.getNode(nodeInfo);
14636         if(!node || !this.isSelected(node)){
14637             //Roo.log("not selected");
14638             return; // not selected.
14639         }
14640         // fireevent???
14641         var ns = [];
14642         Roo.each(this.selections, function(s) {
14643             if (s == node ) {
14644                 Roo.fly(node).removeClass(this.selectedClass);
14645
14646                 return;
14647             }
14648             ns.push(s);
14649         },this);
14650         
14651         this.selections= ns;
14652         this.fireEvent("selectionchange", this, this.selections);
14653     },
14654
14655     /**
14656      * Gets a template node.
14657      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14658      * @return {HTMLElement} The node or null if it wasn't found
14659      */
14660     getNode : function(nodeInfo){
14661         if(typeof nodeInfo == "string"){
14662             return document.getElementById(nodeInfo);
14663         }else if(typeof nodeInfo == "number"){
14664             return this.nodes[nodeInfo];
14665         }
14666         return nodeInfo;
14667     },
14668
14669     /**
14670      * Gets a range template nodes.
14671      * @param {Number} startIndex
14672      * @param {Number} endIndex
14673      * @return {Array} An array of nodes
14674      */
14675     getNodes : function(start, end){
14676         var ns = this.nodes;
14677         start = start || 0;
14678         end = typeof end == "undefined" ? ns.length - 1 : end;
14679         var nodes = [];
14680         if(start <= end){
14681             for(var i = start; i <= end; i++){
14682                 nodes.push(ns[i]);
14683             }
14684         } else{
14685             for(var i = start; i >= end; i--){
14686                 nodes.push(ns[i]);
14687             }
14688         }
14689         return nodes;
14690     },
14691
14692     /**
14693      * Finds the index of the passed node
14694      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14695      * @return {Number} The index of the node or -1
14696      */
14697     indexOf : function(node){
14698         node = this.getNode(node);
14699         if(typeof node.nodeIndex == "number"){
14700             return node.nodeIndex;
14701         }
14702         var ns = this.nodes;
14703         for(var i = 0, len = ns.length; i < len; i++){
14704             if(ns[i] == node){
14705                 return i;
14706             }
14707         }
14708         return -1;
14709     }
14710 });
14711 /*
14712  * - LGPL
14713  *
14714  * based on jquery fullcalendar
14715  * 
14716  */
14717
14718 Roo.bootstrap = Roo.bootstrap || {};
14719 /**
14720  * @class Roo.bootstrap.Calendar
14721  * @extends Roo.bootstrap.Component
14722  * Bootstrap Calendar class
14723  * @cfg {Boolean} loadMask (true|false) default false
14724  * @cfg {Object} header generate the user specific header of the calendar, default false
14725
14726  * @constructor
14727  * Create a new Container
14728  * @param {Object} config The config object
14729  */
14730
14731
14732
14733 Roo.bootstrap.Calendar = function(config){
14734     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14735      this.addEvents({
14736         /**
14737              * @event select
14738              * Fires when a date is selected
14739              * @param {DatePicker} this
14740              * @param {Date} date The selected date
14741              */
14742         'select': true,
14743         /**
14744              * @event monthchange
14745              * Fires when the displayed month changes 
14746              * @param {DatePicker} this
14747              * @param {Date} date The selected month
14748              */
14749         'monthchange': true,
14750         /**
14751              * @event evententer
14752              * Fires when mouse over an event
14753              * @param {Calendar} this
14754              * @param {event} Event
14755              */
14756         'evententer': true,
14757         /**
14758              * @event eventleave
14759              * Fires when the mouse leaves an
14760              * @param {Calendar} this
14761              * @param {event}
14762              */
14763         'eventleave': true,
14764         /**
14765              * @event eventclick
14766              * Fires when the mouse click an
14767              * @param {Calendar} this
14768              * @param {event}
14769              */
14770         'eventclick': true
14771         
14772     });
14773
14774 };
14775
14776 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14777     
14778      /**
14779      * @cfg {Number} startDay
14780      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14781      */
14782     startDay : 0,
14783     
14784     loadMask : false,
14785     
14786     header : false,
14787       
14788     getAutoCreate : function(){
14789         
14790         
14791         var fc_button = function(name, corner, style, content ) {
14792             return Roo.apply({},{
14793                 tag : 'span',
14794                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14795                          (corner.length ?
14796                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14797                             ''
14798                         ),
14799                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14800                 unselectable: 'on'
14801             });
14802         };
14803         
14804         var header = {};
14805         
14806         if(!this.header){
14807             header = {
14808                 tag : 'table',
14809                 cls : 'fc-header',
14810                 style : 'width:100%',
14811                 cn : [
14812                     {
14813                         tag: 'tr',
14814                         cn : [
14815                             {
14816                                 tag : 'td',
14817                                 cls : 'fc-header-left',
14818                                 cn : [
14819                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14820                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14821                                     { tag: 'span', cls: 'fc-header-space' },
14822                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14823
14824
14825                                 ]
14826                             },
14827
14828                             {
14829                                 tag : 'td',
14830                                 cls : 'fc-header-center',
14831                                 cn : [
14832                                     {
14833                                         tag: 'span',
14834                                         cls: 'fc-header-title',
14835                                         cn : {
14836                                             tag: 'H2',
14837                                             html : 'month / year'
14838                                         }
14839                                     }
14840
14841                                 ]
14842                             },
14843                             {
14844                                 tag : 'td',
14845                                 cls : 'fc-header-right',
14846                                 cn : [
14847                               /*      fc_button('month', 'left', '', 'month' ),
14848                                     fc_button('week', '', '', 'week' ),
14849                                     fc_button('day', 'right', '', 'day' )
14850                                 */    
14851
14852                                 ]
14853                             }
14854
14855                         ]
14856                     }
14857                 ]
14858             };
14859         }
14860         
14861         header = this.header;
14862         
14863        
14864         var cal_heads = function() {
14865             var ret = [];
14866             // fixme - handle this.
14867             
14868             for (var i =0; i < Date.dayNames.length; i++) {
14869                 var d = Date.dayNames[i];
14870                 ret.push({
14871                     tag: 'th',
14872                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14873                     html : d.substring(0,3)
14874                 });
14875                 
14876             }
14877             ret[0].cls += ' fc-first';
14878             ret[6].cls += ' fc-last';
14879             return ret;
14880         };
14881         var cal_cell = function(n) {
14882             return  {
14883                 tag: 'td',
14884                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14885                 cn : [
14886                     {
14887                         cn : [
14888                             {
14889                                 cls: 'fc-day-number',
14890                                 html: 'D'
14891                             },
14892                             {
14893                                 cls: 'fc-day-content',
14894                              
14895                                 cn : [
14896                                      {
14897                                         style: 'position: relative;' // height: 17px;
14898                                     }
14899                                 ]
14900                             }
14901                             
14902                             
14903                         ]
14904                     }
14905                 ]
14906                 
14907             }
14908         };
14909         var cal_rows = function() {
14910             
14911             var ret = [];
14912             for (var r = 0; r < 6; r++) {
14913                 var row= {
14914                     tag : 'tr',
14915                     cls : 'fc-week',
14916                     cn : []
14917                 };
14918                 
14919                 for (var i =0; i < Date.dayNames.length; i++) {
14920                     var d = Date.dayNames[i];
14921                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14922
14923                 }
14924                 row.cn[0].cls+=' fc-first';
14925                 row.cn[0].cn[0].style = 'min-height:90px';
14926                 row.cn[6].cls+=' fc-last';
14927                 ret.push(row);
14928                 
14929             }
14930             ret[0].cls += ' fc-first';
14931             ret[4].cls += ' fc-prev-last';
14932             ret[5].cls += ' fc-last';
14933             return ret;
14934             
14935         };
14936         
14937         var cal_table = {
14938             tag: 'table',
14939             cls: 'fc-border-separate',
14940             style : 'width:100%',
14941             cellspacing  : 0,
14942             cn : [
14943                 { 
14944                     tag: 'thead',
14945                     cn : [
14946                         { 
14947                             tag: 'tr',
14948                             cls : 'fc-first fc-last',
14949                             cn : cal_heads()
14950                         }
14951                     ]
14952                 },
14953                 { 
14954                     tag: 'tbody',
14955                     cn : cal_rows()
14956                 }
14957                   
14958             ]
14959         };
14960          
14961          var cfg = {
14962             cls : 'fc fc-ltr',
14963             cn : [
14964                 header,
14965                 {
14966                     cls : 'fc-content',
14967                     style : "position: relative;",
14968                     cn : [
14969                         {
14970                             cls : 'fc-view fc-view-month fc-grid',
14971                             style : 'position: relative',
14972                             unselectable : 'on',
14973                             cn : [
14974                                 {
14975                                     cls : 'fc-event-container',
14976                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14977                                 },
14978                                 cal_table
14979                             ]
14980                         }
14981                     ]
14982     
14983                 }
14984            ] 
14985             
14986         };
14987         
14988          
14989         
14990         return cfg;
14991     },
14992     
14993     
14994     initEvents : function()
14995     {
14996         if(!this.store){
14997             throw "can not find store for calendar";
14998         }
14999         
15000         var mark = {
15001             tag: "div",
15002             cls:"x-dlg-mask",
15003             style: "text-align:center",
15004             cn: [
15005                 {
15006                     tag: "div",
15007                     style: "background-color:white;width:50%;margin:250 auto",
15008                     cn: [
15009                         {
15010                             tag: "img",
15011                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15012                         },
15013                         {
15014                             tag: "span",
15015                             html: "Loading"
15016                         }
15017                         
15018                     ]
15019                 }
15020             ]
15021         };
15022         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15023         
15024         var size = this.el.select('.fc-content', true).first().getSize();
15025         this.maskEl.setSize(size.width, size.height);
15026         this.maskEl.enableDisplayMode("block");
15027         if(!this.loadMask){
15028             this.maskEl.hide();
15029         }
15030         
15031         this.store = Roo.factory(this.store, Roo.data);
15032         this.store.on('load', this.onLoad, this);
15033         this.store.on('beforeload', this.onBeforeLoad, this);
15034         
15035         this.resize();
15036         
15037         this.cells = this.el.select('.fc-day',true);
15038         //Roo.log(this.cells);
15039         this.textNodes = this.el.query('.fc-day-number');
15040         this.cells.addClassOnOver('fc-state-hover');
15041         
15042         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15043         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15044         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15045         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15046         
15047         this.on('monthchange', this.onMonthChange, this);
15048         
15049         this.update(new Date().clearTime());
15050     },
15051     
15052     resize : function() {
15053         var sz  = this.el.getSize();
15054         
15055         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15056         this.el.select('.fc-day-content div',true).setHeight(34);
15057     },
15058     
15059     
15060     // private
15061     showPrevMonth : function(e){
15062         this.update(this.activeDate.add("mo", -1));
15063     },
15064     showToday : function(e){
15065         this.update(new Date().clearTime());
15066     },
15067     // private
15068     showNextMonth : function(e){
15069         this.update(this.activeDate.add("mo", 1));
15070     },
15071
15072     // private
15073     showPrevYear : function(){
15074         this.update(this.activeDate.add("y", -1));
15075     },
15076
15077     // private
15078     showNextYear : function(){
15079         this.update(this.activeDate.add("y", 1));
15080     },
15081
15082     
15083    // private
15084     update : function(date)
15085     {
15086         var vd = this.activeDate;
15087         this.activeDate = date;
15088 //        if(vd && this.el){
15089 //            var t = date.getTime();
15090 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15091 //                Roo.log('using add remove');
15092 //                
15093 //                this.fireEvent('monthchange', this, date);
15094 //                
15095 //                this.cells.removeClass("fc-state-highlight");
15096 //                this.cells.each(function(c){
15097 //                   if(c.dateValue == t){
15098 //                       c.addClass("fc-state-highlight");
15099 //                       setTimeout(function(){
15100 //                            try{c.dom.firstChild.focus();}catch(e){}
15101 //                       }, 50);
15102 //                       return false;
15103 //                   }
15104 //                   return true;
15105 //                });
15106 //                return;
15107 //            }
15108 //        }
15109         
15110         var days = date.getDaysInMonth();
15111         
15112         var firstOfMonth = date.getFirstDateOfMonth();
15113         var startingPos = firstOfMonth.getDay()-this.startDay;
15114         
15115         if(startingPos < this.startDay){
15116             startingPos += 7;
15117         }
15118         
15119         var pm = date.add(Date.MONTH, -1);
15120         var prevStart = pm.getDaysInMonth()-startingPos;
15121 //        
15122         this.cells = this.el.select('.fc-day',true);
15123         this.textNodes = this.el.query('.fc-day-number');
15124         this.cells.addClassOnOver('fc-state-hover');
15125         
15126         var cells = this.cells.elements;
15127         var textEls = this.textNodes;
15128         
15129         Roo.each(cells, function(cell){
15130             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15131         });
15132         
15133         days += startingPos;
15134
15135         // convert everything to numbers so it's fast
15136         var day = 86400000;
15137         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15138         //Roo.log(d);
15139         //Roo.log(pm);
15140         //Roo.log(prevStart);
15141         
15142         var today = new Date().clearTime().getTime();
15143         var sel = date.clearTime().getTime();
15144         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15145         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15146         var ddMatch = this.disabledDatesRE;
15147         var ddText = this.disabledDatesText;
15148         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15149         var ddaysText = this.disabledDaysText;
15150         var format = this.format;
15151         
15152         var setCellClass = function(cal, cell){
15153             cell.row = 0;
15154             cell.events = [];
15155             cell.more = [];
15156             //Roo.log('set Cell Class');
15157             cell.title = "";
15158             var t = d.getTime();
15159             
15160             //Roo.log(d);
15161             
15162             cell.dateValue = t;
15163             if(t == today){
15164                 cell.className += " fc-today";
15165                 cell.className += " fc-state-highlight";
15166                 cell.title = cal.todayText;
15167             }
15168             if(t == sel){
15169                 // disable highlight in other month..
15170                 //cell.className += " fc-state-highlight";
15171                 
15172             }
15173             // disabling
15174             if(t < min) {
15175                 cell.className = " fc-state-disabled";
15176                 cell.title = cal.minText;
15177                 return;
15178             }
15179             if(t > max) {
15180                 cell.className = " fc-state-disabled";
15181                 cell.title = cal.maxText;
15182                 return;
15183             }
15184             if(ddays){
15185                 if(ddays.indexOf(d.getDay()) != -1){
15186                     cell.title = ddaysText;
15187                     cell.className = " fc-state-disabled";
15188                 }
15189             }
15190             if(ddMatch && format){
15191                 var fvalue = d.dateFormat(format);
15192                 if(ddMatch.test(fvalue)){
15193                     cell.title = ddText.replace("%0", fvalue);
15194                     cell.className = " fc-state-disabled";
15195                 }
15196             }
15197             
15198             if (!cell.initialClassName) {
15199                 cell.initialClassName = cell.dom.className;
15200             }
15201             
15202             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15203         };
15204
15205         var i = 0;
15206         
15207         for(; i < startingPos; i++) {
15208             textEls[i].innerHTML = (++prevStart);
15209             d.setDate(d.getDate()+1);
15210             
15211             cells[i].className = "fc-past fc-other-month";
15212             setCellClass(this, cells[i]);
15213         }
15214         
15215         var intDay = 0;
15216         
15217         for(; i < days; i++){
15218             intDay = i - startingPos + 1;
15219             textEls[i].innerHTML = (intDay);
15220             d.setDate(d.getDate()+1);
15221             
15222             cells[i].className = ''; // "x-date-active";
15223             setCellClass(this, cells[i]);
15224         }
15225         var extraDays = 0;
15226         
15227         for(; i < 42; i++) {
15228             textEls[i].innerHTML = (++extraDays);
15229             d.setDate(d.getDate()+1);
15230             
15231             cells[i].className = "fc-future fc-other-month";
15232             setCellClass(this, cells[i]);
15233         }
15234         
15235         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15236         
15237         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15238         
15239         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15240         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15241         
15242         if(totalRows != 6){
15243             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15244             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15245         }
15246         
15247         this.fireEvent('monthchange', this, date);
15248         
15249         
15250         /*
15251         if(!this.internalRender){
15252             var main = this.el.dom.firstChild;
15253             var w = main.offsetWidth;
15254             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15255             Roo.fly(main).setWidth(w);
15256             this.internalRender = true;
15257             // opera does not respect the auto grow header center column
15258             // then, after it gets a width opera refuses to recalculate
15259             // without a second pass
15260             if(Roo.isOpera && !this.secondPass){
15261                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15262                 this.secondPass = true;
15263                 this.update.defer(10, this, [date]);
15264             }
15265         }
15266         */
15267         
15268     },
15269     
15270     findCell : function(dt) {
15271         dt = dt.clearTime().getTime();
15272         var ret = false;
15273         this.cells.each(function(c){
15274             //Roo.log("check " +c.dateValue + '?=' + dt);
15275             if(c.dateValue == dt){
15276                 ret = c;
15277                 return false;
15278             }
15279             return true;
15280         });
15281         
15282         return ret;
15283     },
15284     
15285     findCells : function(ev) {
15286         var s = ev.start.clone().clearTime().getTime();
15287        // Roo.log(s);
15288         var e= ev.end.clone().clearTime().getTime();
15289        // Roo.log(e);
15290         var ret = [];
15291         this.cells.each(function(c){
15292              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15293             
15294             if(c.dateValue > e){
15295                 return ;
15296             }
15297             if(c.dateValue < s){
15298                 return ;
15299             }
15300             ret.push(c);
15301         });
15302         
15303         return ret;    
15304     },
15305     
15306 //    findBestRow: function(cells)
15307 //    {
15308 //        var ret = 0;
15309 //        
15310 //        for (var i =0 ; i < cells.length;i++) {
15311 //            ret  = Math.max(cells[i].rows || 0,ret);
15312 //        }
15313 //        return ret;
15314 //        
15315 //    },
15316     
15317     
15318     addItem : function(ev)
15319     {
15320         // look for vertical location slot in
15321         var cells = this.findCells(ev);
15322         
15323 //        ev.row = this.findBestRow(cells);
15324         
15325         // work out the location.
15326         
15327         var crow = false;
15328         var rows = [];
15329         for(var i =0; i < cells.length; i++) {
15330             
15331             cells[i].row = cells[0].row;
15332             
15333             if(i == 0){
15334                 cells[i].row = cells[i].row + 1;
15335             }
15336             
15337             if (!crow) {
15338                 crow = {
15339                     start : cells[i],
15340                     end :  cells[i]
15341                 };
15342                 continue;
15343             }
15344             if (crow.start.getY() == cells[i].getY()) {
15345                 // on same row.
15346                 crow.end = cells[i];
15347                 continue;
15348             }
15349             // different row.
15350             rows.push(crow);
15351             crow = {
15352                 start: cells[i],
15353                 end : cells[i]
15354             };
15355             
15356         }
15357         
15358         rows.push(crow);
15359         ev.els = [];
15360         ev.rows = rows;
15361         ev.cells = cells;
15362         
15363         cells[0].events.push(ev);
15364         
15365         this.calevents.push(ev);
15366     },
15367     
15368     clearEvents: function() {
15369         
15370         if(!this.calevents){
15371             return;
15372         }
15373         
15374         Roo.each(this.cells.elements, function(c){
15375             c.row = 0;
15376             c.events = [];
15377             c.more = [];
15378         });
15379         
15380         Roo.each(this.calevents, function(e) {
15381             Roo.each(e.els, function(el) {
15382                 el.un('mouseenter' ,this.onEventEnter, this);
15383                 el.un('mouseleave' ,this.onEventLeave, this);
15384                 el.remove();
15385             },this);
15386         },this);
15387         
15388         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15389             e.remove();
15390         });
15391         
15392     },
15393     
15394     renderEvents: function()
15395     {   
15396         var _this = this;
15397         
15398         this.cells.each(function(c) {
15399             
15400             if(c.row < 5){
15401                 return;
15402             }
15403             
15404             var ev = c.events;
15405             
15406             var r = 4;
15407             if(c.row != c.events.length){
15408                 r = 4 - (4 - (c.row - c.events.length));
15409             }
15410             
15411             c.events = ev.slice(0, r);
15412             c.more = ev.slice(r);
15413             
15414             if(c.more.length && c.more.length == 1){
15415                 c.events.push(c.more.pop());
15416             }
15417             
15418             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15419             
15420         });
15421             
15422         this.cells.each(function(c) {
15423             
15424             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15425             
15426             
15427             for (var e = 0; e < c.events.length; e++){
15428                 var ev = c.events[e];
15429                 var rows = ev.rows;
15430                 
15431                 for(var i = 0; i < rows.length; i++) {
15432                 
15433                     // how many rows should it span..
15434
15435                     var  cfg = {
15436                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15437                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15438
15439                         unselectable : "on",
15440                         cn : [
15441                             {
15442                                 cls: 'fc-event-inner',
15443                                 cn : [
15444     //                                {
15445     //                                  tag:'span',
15446     //                                  cls: 'fc-event-time',
15447     //                                  html : cells.length > 1 ? '' : ev.time
15448     //                                },
15449                                     {
15450                                       tag:'span',
15451                                       cls: 'fc-event-title',
15452                                       html : String.format('{0}', ev.title)
15453                                     }
15454
15455
15456                                 ]
15457                             },
15458                             {
15459                                 cls: 'ui-resizable-handle ui-resizable-e',
15460                                 html : '&nbsp;&nbsp;&nbsp'
15461                             }
15462
15463                         ]
15464                     };
15465
15466                     if (i == 0) {
15467                         cfg.cls += ' fc-event-start';
15468                     }
15469                     if ((i+1) == rows.length) {
15470                         cfg.cls += ' fc-event-end';
15471                     }
15472
15473                     var ctr = _this.el.select('.fc-event-container',true).first();
15474                     var cg = ctr.createChild(cfg);
15475
15476                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15477                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15478
15479                     var r = (c.more.length) ? 1 : 0;
15480                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15481                     cg.setWidth(ebox.right - sbox.x -2);
15482
15483                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15484                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15485                     cg.on('click', _this.onEventClick, _this, ev);
15486
15487                     ev.els.push(cg);
15488                     
15489                 }
15490                 
15491             }
15492             
15493             
15494             if(c.more.length){
15495                 var  cfg = {
15496                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15497                     style : 'position: absolute',
15498                     unselectable : "on",
15499                     cn : [
15500                         {
15501                             cls: 'fc-event-inner',
15502                             cn : [
15503                                 {
15504                                   tag:'span',
15505                                   cls: 'fc-event-title',
15506                                   html : 'More'
15507                                 }
15508
15509
15510                             ]
15511                         },
15512                         {
15513                             cls: 'ui-resizable-handle ui-resizable-e',
15514                             html : '&nbsp;&nbsp;&nbsp'
15515                         }
15516
15517                     ]
15518                 };
15519
15520                 var ctr = _this.el.select('.fc-event-container',true).first();
15521                 var cg = ctr.createChild(cfg);
15522
15523                 var sbox = c.select('.fc-day-content',true).first().getBox();
15524                 var ebox = c.select('.fc-day-content',true).first().getBox();
15525                 //Roo.log(cg);
15526                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15527                 cg.setWidth(ebox.right - sbox.x -2);
15528
15529                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15530                 
15531             }
15532             
15533         });
15534         
15535         
15536         
15537     },
15538     
15539     onEventEnter: function (e, el,event,d) {
15540         this.fireEvent('evententer', this, el, event);
15541     },
15542     
15543     onEventLeave: function (e, el,event,d) {
15544         this.fireEvent('eventleave', this, el, event);
15545     },
15546     
15547     onEventClick: function (e, el,event,d) {
15548         this.fireEvent('eventclick', this, el, event);
15549     },
15550     
15551     onMonthChange: function () {
15552         this.store.load();
15553     },
15554     
15555     onMoreEventClick: function(e, el, more)
15556     {
15557         var _this = this;
15558         
15559         this.calpopover.placement = 'right';
15560         this.calpopover.setTitle('More');
15561         
15562         this.calpopover.setContent('');
15563         
15564         var ctr = this.calpopover.el.select('.popover-content', true).first();
15565         
15566         Roo.each(more, function(m){
15567             var cfg = {
15568                 cls : 'fc-event-hori fc-event-draggable',
15569                 html : m.title
15570             };
15571             var cg = ctr.createChild(cfg);
15572             
15573             cg.on('click', _this.onEventClick, _this, m);
15574         });
15575         
15576         this.calpopover.show(el);
15577         
15578         
15579     },
15580     
15581     onLoad: function () 
15582     {   
15583         this.calevents = [];
15584         var cal = this;
15585         
15586         if(this.store.getCount() > 0){
15587             this.store.data.each(function(d){
15588                cal.addItem({
15589                     id : d.data.id,
15590                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15591                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15592                     time : d.data.start_time,
15593                     title : d.data.title,
15594                     description : d.data.description,
15595                     venue : d.data.venue
15596                 });
15597             });
15598         }
15599         
15600         this.renderEvents();
15601         
15602         if(this.calevents.length && this.loadMask){
15603             this.maskEl.hide();
15604         }
15605     },
15606     
15607     onBeforeLoad: function()
15608     {
15609         this.clearEvents();
15610         if(this.loadMask){
15611             this.maskEl.show();
15612         }
15613     }
15614 });
15615
15616  
15617  /*
15618  * - LGPL
15619  *
15620  * element
15621  * 
15622  */
15623
15624 /**
15625  * @class Roo.bootstrap.Popover
15626  * @extends Roo.bootstrap.Component
15627  * Bootstrap Popover class
15628  * @cfg {String} html contents of the popover   (or false to use children..)
15629  * @cfg {String} title of popover (or false to hide)
15630  * @cfg {String} placement how it is placed
15631  * @cfg {String} trigger click || hover (or false to trigger manually)
15632  * @cfg {String} over what (parent or false to trigger manually.)
15633  * @cfg {Number} delay - delay before showing
15634  
15635  * @constructor
15636  * Create a new Popover
15637  * @param {Object} config The config object
15638  */
15639
15640 Roo.bootstrap.Popover = function(config){
15641     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15642 };
15643
15644 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15645     
15646     title: 'Fill in a title',
15647     html: false,
15648     
15649     placement : 'right',
15650     trigger : 'hover', // hover
15651     
15652     delay : 0,
15653     
15654     over: 'parent',
15655     
15656     can_build_overlaid : false,
15657     
15658     getChildContainer : function()
15659     {
15660         return this.el.select('.popover-content',true).first();
15661     },
15662     
15663     getAutoCreate : function(){
15664          
15665         var cfg = {
15666            cls : 'popover roo-dynamic',
15667            style: 'display:block',
15668            cn : [
15669                 {
15670                     cls : 'arrow'
15671                 },
15672                 {
15673                     cls : 'popover-inner',
15674                     cn : [
15675                         {
15676                             tag: 'h3',
15677                             cls: 'popover-title',
15678                             html : this.title
15679                         },
15680                         {
15681                             cls : 'popover-content',
15682                             html : this.html
15683                         }
15684                     ]
15685                     
15686                 }
15687            ]
15688         };
15689         
15690         return cfg;
15691     },
15692     setTitle: function(str)
15693     {
15694         this.title = str;
15695         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15696     },
15697     setContent: function(str)
15698     {
15699         this.html = str;
15700         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15701     },
15702     // as it get's added to the bottom of the page.
15703     onRender : function(ct, position)
15704     {
15705         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15706         if(!this.el){
15707             var cfg = Roo.apply({},  this.getAutoCreate());
15708             cfg.id = Roo.id();
15709             
15710             if (this.cls) {
15711                 cfg.cls += ' ' + this.cls;
15712             }
15713             if (this.style) {
15714                 cfg.style = this.style;
15715             }
15716             //Roo.log("adding to ");
15717             this.el = Roo.get(document.body).createChild(cfg, position);
15718 //            Roo.log(this.el);
15719         }
15720         this.initEvents();
15721     },
15722     
15723     initEvents : function()
15724     {
15725         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15726         this.el.enableDisplayMode('block');
15727         this.el.hide();
15728         if (this.over === false) {
15729             return; 
15730         }
15731         if (this.triggers === false) {
15732             return;
15733         }
15734         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15735         var triggers = this.trigger ? this.trigger.split(' ') : [];
15736         Roo.each(triggers, function(trigger) {
15737         
15738             if (trigger == 'click') {
15739                 on_el.on('click', this.toggle, this);
15740             } else if (trigger != 'manual') {
15741                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15742                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15743       
15744                 on_el.on(eventIn  ,this.enter, this);
15745                 on_el.on(eventOut, this.leave, this);
15746             }
15747         }, this);
15748         
15749     },
15750     
15751     
15752     // private
15753     timeout : null,
15754     hoverState : null,
15755     
15756     toggle : function () {
15757         this.hoverState == 'in' ? this.leave() : this.enter();
15758     },
15759     
15760     enter : function () {
15761        
15762     
15763         clearTimeout(this.timeout);
15764     
15765         this.hoverState = 'in';
15766     
15767         if (!this.delay || !this.delay.show) {
15768             this.show();
15769             return;
15770         }
15771         var _t = this;
15772         this.timeout = setTimeout(function () {
15773             if (_t.hoverState == 'in') {
15774                 _t.show();
15775             }
15776         }, this.delay.show)
15777     },
15778     leave : function() {
15779         clearTimeout(this.timeout);
15780     
15781         this.hoverState = 'out';
15782     
15783         if (!this.delay || !this.delay.hide) {
15784             this.hide();
15785             return;
15786         }
15787         var _t = this;
15788         this.timeout = setTimeout(function () {
15789             if (_t.hoverState == 'out') {
15790                 _t.hide();
15791             }
15792         }, this.delay.hide)
15793     },
15794     
15795     show : function (on_el)
15796     {
15797         if (!on_el) {
15798             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15799         }
15800         // set content.
15801         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15802         if (this.html !== false) {
15803             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15804         }
15805         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15806         if (!this.title.length) {
15807             this.el.select('.popover-title',true).hide();
15808         }
15809         
15810         var placement = typeof this.placement == 'function' ?
15811             this.placement.call(this, this.el, on_el) :
15812             this.placement;
15813             
15814         var autoToken = /\s?auto?\s?/i;
15815         var autoPlace = autoToken.test(placement);
15816         if (autoPlace) {
15817             placement = placement.replace(autoToken, '') || 'top';
15818         }
15819         
15820         //this.el.detach()
15821         //this.el.setXY([0,0]);
15822         this.el.show();
15823         this.el.dom.style.display='block';
15824         this.el.addClass(placement);
15825         
15826         //this.el.appendTo(on_el);
15827         
15828         var p = this.getPosition();
15829         var box = this.el.getBox();
15830         
15831         if (autoPlace) {
15832             // fixme..
15833         }
15834         var align = Roo.bootstrap.Popover.alignment[placement];
15835         this.el.alignTo(on_el, align[0],align[1]);
15836         //var arrow = this.el.select('.arrow',true).first();
15837         //arrow.set(align[2], 
15838         
15839         this.el.addClass('in');
15840         
15841         
15842         if (this.el.hasClass('fade')) {
15843             // fade it?
15844         }
15845         
15846     },
15847     hide : function()
15848     {
15849         this.el.setXY([0,0]);
15850         this.el.removeClass('in');
15851         this.el.hide();
15852         this.hoverState = null;
15853         
15854     }
15855     
15856 });
15857
15858 Roo.bootstrap.Popover.alignment = {
15859     'left' : ['r-l', [-10,0], 'right'],
15860     'right' : ['l-r', [10,0], 'left'],
15861     'bottom' : ['t-b', [0,10], 'top'],
15862     'top' : [ 'b-t', [0,-10], 'bottom']
15863 };
15864
15865  /*
15866  * - LGPL
15867  *
15868  * Progress
15869  * 
15870  */
15871
15872 /**
15873  * @class Roo.bootstrap.Progress
15874  * @extends Roo.bootstrap.Component
15875  * Bootstrap Progress class
15876  * @cfg {Boolean} striped striped of the progress bar
15877  * @cfg {Boolean} active animated of the progress bar
15878  * 
15879  * 
15880  * @constructor
15881  * Create a new Progress
15882  * @param {Object} config The config object
15883  */
15884
15885 Roo.bootstrap.Progress = function(config){
15886     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15887 };
15888
15889 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15890     
15891     striped : false,
15892     active: false,
15893     
15894     getAutoCreate : function(){
15895         var cfg = {
15896             tag: 'div',
15897             cls: 'progress'
15898         };
15899         
15900         
15901         if(this.striped){
15902             cfg.cls += ' progress-striped';
15903         }
15904       
15905         if(this.active){
15906             cfg.cls += ' active';
15907         }
15908         
15909         
15910         return cfg;
15911     }
15912    
15913 });
15914
15915  
15916
15917  /*
15918  * - LGPL
15919  *
15920  * ProgressBar
15921  * 
15922  */
15923
15924 /**
15925  * @class Roo.bootstrap.ProgressBar
15926  * @extends Roo.bootstrap.Component
15927  * Bootstrap ProgressBar class
15928  * @cfg {Number} aria_valuenow aria-value now
15929  * @cfg {Number} aria_valuemin aria-value min
15930  * @cfg {Number} aria_valuemax aria-value max
15931  * @cfg {String} label label for the progress bar
15932  * @cfg {String} panel (success | info | warning | danger )
15933  * @cfg {String} role role of the progress bar
15934  * @cfg {String} sr_only text
15935  * 
15936  * 
15937  * @constructor
15938  * Create a new ProgressBar
15939  * @param {Object} config The config object
15940  */
15941
15942 Roo.bootstrap.ProgressBar = function(config){
15943     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15944 };
15945
15946 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15947     
15948     aria_valuenow : 0,
15949     aria_valuemin : 0,
15950     aria_valuemax : 100,
15951     label : false,
15952     panel : false,
15953     role : false,
15954     sr_only: false,
15955     
15956     getAutoCreate : function()
15957     {
15958         
15959         var cfg = {
15960             tag: 'div',
15961             cls: 'progress-bar',
15962             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15963         };
15964         
15965         if(this.sr_only){
15966             cfg.cn = {
15967                 tag: 'span',
15968                 cls: 'sr-only',
15969                 html: this.sr_only
15970             }
15971         }
15972         
15973         if(this.role){
15974             cfg.role = this.role;
15975         }
15976         
15977         if(this.aria_valuenow){
15978             cfg['aria-valuenow'] = this.aria_valuenow;
15979         }
15980         
15981         if(this.aria_valuemin){
15982             cfg['aria-valuemin'] = this.aria_valuemin;
15983         }
15984         
15985         if(this.aria_valuemax){
15986             cfg['aria-valuemax'] = this.aria_valuemax;
15987         }
15988         
15989         if(this.label && !this.sr_only){
15990             cfg.html = this.label;
15991         }
15992         
15993         if(this.panel){
15994             cfg.cls += ' progress-bar-' + this.panel;
15995         }
15996         
15997         return cfg;
15998     },
15999     
16000     update : function(aria_valuenow)
16001     {
16002         this.aria_valuenow = aria_valuenow;
16003         
16004         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16005     }
16006    
16007 });
16008
16009  
16010
16011  /*
16012  * - LGPL
16013  *
16014  * column
16015  * 
16016  */
16017
16018 /**
16019  * @class Roo.bootstrap.TabGroup
16020  * @extends Roo.bootstrap.Column
16021  * Bootstrap Column class
16022  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16023  * @cfg {Boolean} carousel true to make the group behave like a carousel
16024  * @cfg {Boolean} bullets show bullets for the panels
16025  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16026  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16027  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16028  * 
16029  * @constructor
16030  * Create a new TabGroup
16031  * @param {Object} config The config object
16032  */
16033
16034 Roo.bootstrap.TabGroup = function(config){
16035     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16036     if (!this.navId) {
16037         this.navId = Roo.id();
16038     }
16039     this.tabs = [];
16040     Roo.bootstrap.TabGroup.register(this);
16041     
16042 };
16043
16044 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16045     
16046     carousel : false,
16047     transition : false,
16048     bullets : 0,
16049     timer : 0,
16050     autoslide : false,
16051     slideFn : false,
16052     slideOnTouch : false,
16053     
16054     getAutoCreate : function()
16055     {
16056         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16057         
16058         cfg.cls += ' tab-content';
16059         
16060         if (this.carousel) {
16061             cfg.cls += ' carousel slide';
16062             
16063             cfg.cn = [{
16064                cls : 'carousel-inner'
16065             }];
16066         
16067             if(this.bullets  && !Roo.isTouch){
16068                 
16069                 var bullets = {
16070                     cls : 'carousel-bullets',
16071                     cn : []
16072                 };
16073                
16074                 if(this.bullets_cls){
16075                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16076                 }
16077                  /*
16078                 for (var i = 0; i < this.bullets; i++){
16079                     bullets.cn.push({
16080                         cls : 'bullet bullet-' + i
16081                     });
16082                 }
16083                 */
16084                 bullets.cn.push({
16085                     cls : 'clear'
16086                 });
16087                 
16088                 cfg.cn[0].cn = bullets;
16089             }
16090         }
16091         
16092         return cfg;
16093     },
16094     
16095     initEvents:  function()
16096     {
16097         if(Roo.isTouch && this.slideOnTouch){
16098             this.el.on("touchstart", this.onTouchStart, this);
16099         }
16100         
16101         if(this.autoslide){
16102             var _this = this;
16103             
16104             this.slideFn = window.setInterval(function() {
16105                 _this.showPanelNext();
16106             }, this.timer);
16107         }
16108         
16109     },
16110     
16111     onTouchStart : function(e, el, o)
16112     {
16113         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16114             return;
16115         }
16116         
16117         this.showPanelNext();
16118     },
16119     
16120     getChildContainer : function()
16121     {
16122         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16123     },
16124     
16125     /**
16126     * register a Navigation item
16127     * @param {Roo.bootstrap.NavItem} the navitem to add
16128     */
16129     register : function(item)
16130     {
16131         this.tabs.push( item);
16132         item.navId = this.navId; // not really needed..
16133         this.addBullet();
16134     
16135     },
16136     
16137     getActivePanel : function()
16138     {
16139         var r = false;
16140         Roo.each(this.tabs, function(t) {
16141             if (t.active) {
16142                 r = t;
16143                 return false;
16144             }
16145             return null;
16146         });
16147         return r;
16148         
16149     },
16150     getPanelByName : function(n)
16151     {
16152         var r = false;
16153         Roo.each(this.tabs, function(t) {
16154             if (t.tabId == n) {
16155                 r = t;
16156                 return false;
16157             }
16158             return null;
16159         });
16160         return r;
16161     },
16162     indexOfPanel : function(p)
16163     {
16164         var r = false;
16165         Roo.each(this.tabs, function(t,i) {
16166             if (t.tabId == p.tabId) {
16167                 r = i;
16168                 return false;
16169             }
16170             return null;
16171         });
16172         return r;
16173     },
16174     /**
16175      * show a specific panel
16176      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16177      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16178      */
16179     showPanel : function (pan)
16180     {
16181         if(this.transition || typeof(pan) == 'undefined'){
16182             Roo.log("waiting for the transitionend");
16183             return;
16184         }
16185         
16186         if (typeof(pan) == 'number') {
16187             pan = this.tabs[pan];
16188         }
16189         
16190         if (typeof(pan) == 'string') {
16191             pan = this.getPanelByName(pan);
16192         }
16193         
16194         var cur = this.getActivePanel();
16195         
16196         if(!pan || !cur){
16197             Roo.log('pan or acitve pan is undefined');
16198             return false;
16199         }
16200         
16201         if (pan.tabId == this.getActivePanel().tabId) {
16202             return true;
16203         }
16204         
16205         if (false === cur.fireEvent('beforedeactivate')) {
16206             return false;
16207         }
16208         
16209         if(this.bullets > 0 && !Roo.isTouch){
16210             this.setActiveBullet(this.indexOfPanel(pan));
16211         }
16212         
16213         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16214             
16215             this.transition = true;
16216             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16217             var lr = dir == 'next' ? 'left' : 'right';
16218             pan.el.addClass(dir); // or prev
16219             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16220             cur.el.addClass(lr); // or right
16221             pan.el.addClass(lr);
16222             
16223             var _this = this;
16224             cur.el.on('transitionend', function() {
16225                 Roo.log("trans end?");
16226                 
16227                 pan.el.removeClass([lr,dir]);
16228                 pan.setActive(true);
16229                 
16230                 cur.el.removeClass([lr]);
16231                 cur.setActive(false);
16232                 
16233                 _this.transition = false;
16234                 
16235             }, this, { single:  true } );
16236             
16237             return true;
16238         }
16239         
16240         cur.setActive(false);
16241         pan.setActive(true);
16242         
16243         return true;
16244         
16245     },
16246     showPanelNext : function()
16247     {
16248         var i = this.indexOfPanel(this.getActivePanel());
16249         
16250         if (i >= this.tabs.length - 1 && !this.autoslide) {
16251             return;
16252         }
16253         
16254         if (i >= this.tabs.length - 1 && this.autoslide) {
16255             i = -1;
16256         }
16257         
16258         this.showPanel(this.tabs[i+1]);
16259     },
16260     
16261     showPanelPrev : function()
16262     {
16263         var i = this.indexOfPanel(this.getActivePanel());
16264         
16265         if (i  < 1 && !this.autoslide) {
16266             return;
16267         }
16268         
16269         if (i < 1 && this.autoslide) {
16270             i = this.tabs.length;
16271         }
16272         
16273         this.showPanel(this.tabs[i-1]);
16274     },
16275     
16276     
16277     addBullet: function()
16278     {
16279         if(!this.bullets || Roo.isTouch){
16280             return;
16281         }
16282         var ctr = this.el.select('.carousel-bullets',true).first();
16283         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16284         var bullet = ctr.createChild({
16285             cls : 'bullet bullet-' + i
16286         },ctr.dom.lastChild);
16287         
16288         
16289         var _this = this;
16290         
16291         bullet.on('click', (function(e, el, o, ii, t){
16292
16293             e.preventDefault();
16294
16295             this.showPanel(ii);
16296
16297             if(this.autoslide && this.slideFn){
16298                 clearInterval(this.slideFn);
16299                 this.slideFn = window.setInterval(function() {
16300                     _this.showPanelNext();
16301                 }, this.timer);
16302             }
16303
16304         }).createDelegate(this, [i, bullet], true));
16305                 
16306         
16307     },
16308      
16309     setActiveBullet : function(i)
16310     {
16311         if(Roo.isTouch){
16312             return;
16313         }
16314         
16315         Roo.each(this.el.select('.bullet', true).elements, function(el){
16316             el.removeClass('selected');
16317         });
16318
16319         var bullet = this.el.select('.bullet-' + i, true).first();
16320         
16321         if(!bullet){
16322             return;
16323         }
16324         
16325         bullet.addClass('selected');
16326     }
16327     
16328     
16329   
16330 });
16331
16332  
16333
16334  
16335  
16336 Roo.apply(Roo.bootstrap.TabGroup, {
16337     
16338     groups: {},
16339      /**
16340     * register a Navigation Group
16341     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16342     */
16343     register : function(navgrp)
16344     {
16345         this.groups[navgrp.navId] = navgrp;
16346         
16347     },
16348     /**
16349     * fetch a Navigation Group based on the navigation ID
16350     * if one does not exist , it will get created.
16351     * @param {string} the navgroup to add
16352     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16353     */
16354     get: function(navId) {
16355         if (typeof(this.groups[navId]) == 'undefined') {
16356             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16357         }
16358         return this.groups[navId] ;
16359     }
16360     
16361     
16362     
16363 });
16364
16365  /*
16366  * - LGPL
16367  *
16368  * TabPanel
16369  * 
16370  */
16371
16372 /**
16373  * @class Roo.bootstrap.TabPanel
16374  * @extends Roo.bootstrap.Component
16375  * Bootstrap TabPanel class
16376  * @cfg {Boolean} active panel active
16377  * @cfg {String} html panel content
16378  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16379  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16380  * 
16381  * 
16382  * @constructor
16383  * Create a new TabPanel
16384  * @param {Object} config The config object
16385  */
16386
16387 Roo.bootstrap.TabPanel = function(config){
16388     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16389     this.addEvents({
16390         /**
16391              * @event changed
16392              * Fires when the active status changes
16393              * @param {Roo.bootstrap.TabPanel} this
16394              * @param {Boolean} state the new state
16395             
16396          */
16397         'changed': true,
16398         /**
16399              * @event beforedeactivate
16400              * Fires before a tab is de-activated - can be used to do validation on a form.
16401              * @param {Roo.bootstrap.TabPanel} this
16402              * @return {Boolean} false if there is an error
16403             
16404          */
16405         'beforedeactivate': true
16406      });
16407     
16408     this.tabId = this.tabId || Roo.id();
16409   
16410 };
16411
16412 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16413     
16414     active: false,
16415     html: false,
16416     tabId: false,
16417     navId : false,
16418     
16419     getAutoCreate : function(){
16420         var cfg = {
16421             tag: 'div',
16422             // item is needed for carousel - not sure if it has any effect otherwise
16423             cls: 'tab-pane item',
16424             html: this.html || ''
16425         };
16426         
16427         if(this.active){
16428             cfg.cls += ' active';
16429         }
16430         
16431         if(this.tabId){
16432             cfg.tabId = this.tabId;
16433         }
16434         
16435         
16436         return cfg;
16437     },
16438     
16439     initEvents:  function()
16440     {
16441         var p = this.parent();
16442         this.navId = this.navId || p.navId;
16443         
16444         if (typeof(this.navId) != 'undefined') {
16445             // not really needed.. but just in case.. parent should be a NavGroup.
16446             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16447             
16448             tg.register(this);
16449             
16450             var i = tg.tabs.length - 1;
16451             
16452             if(this.active && tg.bullets > 0 && i < tg.bullets){
16453                 tg.setActiveBullet(i);
16454             }
16455         }
16456         
16457     },
16458     
16459     
16460     onRender : function(ct, position)
16461     {
16462        // Roo.log("Call onRender: " + this.xtype);
16463         
16464         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16465         
16466         
16467         
16468         
16469         
16470     },
16471     
16472     setActive: function(state)
16473     {
16474         Roo.log("panel - set active " + this.tabId + "=" + state);
16475         
16476         this.active = state;
16477         if (!state) {
16478             this.el.removeClass('active');
16479             
16480         } else  if (!this.el.hasClass('active')) {
16481             this.el.addClass('active');
16482         }
16483         
16484         this.fireEvent('changed', this, state);
16485     }
16486     
16487     
16488 });
16489  
16490
16491  
16492
16493  /*
16494  * - LGPL
16495  *
16496  * DateField
16497  * 
16498  */
16499
16500 /**
16501  * @class Roo.bootstrap.DateField
16502  * @extends Roo.bootstrap.Input
16503  * Bootstrap DateField class
16504  * @cfg {Number} weekStart default 0
16505  * @cfg {String} viewMode default empty, (months|years)
16506  * @cfg {String} minViewMode default empty, (months|years)
16507  * @cfg {Number} startDate default -Infinity
16508  * @cfg {Number} endDate default Infinity
16509  * @cfg {Boolean} todayHighlight default false
16510  * @cfg {Boolean} todayBtn default false
16511  * @cfg {Boolean} calendarWeeks default false
16512  * @cfg {Object} daysOfWeekDisabled default empty
16513  * @cfg {Boolean} singleMode default false (true | false)
16514  * 
16515  * @cfg {Boolean} keyboardNavigation default true
16516  * @cfg {String} language default en
16517  * 
16518  * @constructor
16519  * Create a new DateField
16520  * @param {Object} config The config object
16521  */
16522
16523 Roo.bootstrap.DateField = function(config){
16524     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16525      this.addEvents({
16526             /**
16527              * @event show
16528              * Fires when this field show.
16529              * @param {Roo.bootstrap.DateField} this
16530              * @param {Mixed} date The date value
16531              */
16532             show : true,
16533             /**
16534              * @event show
16535              * Fires when this field hide.
16536              * @param {Roo.bootstrap.DateField} this
16537              * @param {Mixed} date The date value
16538              */
16539             hide : true,
16540             /**
16541              * @event select
16542              * Fires when select a date.
16543              * @param {Roo.bootstrap.DateField} this
16544              * @param {Mixed} date The date value
16545              */
16546             select : true,
16547             /**
16548              * @event beforeselect
16549              * Fires when before select a date.
16550              * @param {Roo.bootstrap.DateField} this
16551              * @param {Mixed} date The date value
16552              */
16553             beforeselect : true
16554         });
16555 };
16556
16557 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16558     
16559     /**
16560      * @cfg {String} format
16561      * The default date format string which can be overriden for localization support.  The format must be
16562      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16563      */
16564     format : "m/d/y",
16565     /**
16566      * @cfg {String} altFormats
16567      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16568      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16569      */
16570     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16571     
16572     weekStart : 0,
16573     
16574     viewMode : '',
16575     
16576     minViewMode : '',
16577     
16578     todayHighlight : false,
16579     
16580     todayBtn: false,
16581     
16582     language: 'en',
16583     
16584     keyboardNavigation: true,
16585     
16586     calendarWeeks: false,
16587     
16588     startDate: -Infinity,
16589     
16590     endDate: Infinity,
16591     
16592     daysOfWeekDisabled: [],
16593     
16594     _events: [],
16595     
16596     singleMode : false,
16597     
16598     UTCDate: function()
16599     {
16600         return new Date(Date.UTC.apply(Date, arguments));
16601     },
16602     
16603     UTCToday: function()
16604     {
16605         var today = new Date();
16606         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16607     },
16608     
16609     getDate: function() {
16610             var d = this.getUTCDate();
16611             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16612     },
16613     
16614     getUTCDate: function() {
16615             return this.date;
16616     },
16617     
16618     setDate: function(d) {
16619             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16620     },
16621     
16622     setUTCDate: function(d) {
16623             this.date = d;
16624             this.setValue(this.formatDate(this.date));
16625     },
16626         
16627     onRender: function(ct, position)
16628     {
16629         
16630         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16631         
16632         this.language = this.language || 'en';
16633         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16634         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16635         
16636         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16637         this.format = this.format || 'm/d/y';
16638         this.isInline = false;
16639         this.isInput = true;
16640         this.component = this.el.select('.add-on', true).first() || false;
16641         this.component = (this.component && this.component.length === 0) ? false : this.component;
16642         this.hasInput = this.component && this.inputEL().length;
16643         
16644         if (typeof(this.minViewMode === 'string')) {
16645             switch (this.minViewMode) {
16646                 case 'months':
16647                     this.minViewMode = 1;
16648                     break;
16649                 case 'years':
16650                     this.minViewMode = 2;
16651                     break;
16652                 default:
16653                     this.minViewMode = 0;
16654                     break;
16655             }
16656         }
16657         
16658         if (typeof(this.viewMode === 'string')) {
16659             switch (this.viewMode) {
16660                 case 'months':
16661                     this.viewMode = 1;
16662                     break;
16663                 case 'years':
16664                     this.viewMode = 2;
16665                     break;
16666                 default:
16667                     this.viewMode = 0;
16668                     break;
16669             }
16670         }
16671                 
16672         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16673         
16674 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16675         
16676         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16677         
16678         this.picker().on('mousedown', this.onMousedown, this);
16679         this.picker().on('click', this.onClick, this);
16680         
16681         this.picker().addClass('datepicker-dropdown');
16682         
16683         this.startViewMode = this.viewMode;
16684         
16685         if(this.singleMode){
16686             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16687                 v.setVisibilityMode(Roo.Element.DISPLAY);
16688                 v.hide();
16689             });
16690             
16691             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16692                 v.setStyle('width', '189px');
16693             });
16694         }
16695         
16696         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16697             if(!this.calendarWeeks){
16698                 v.remove();
16699                 return;
16700             }
16701             
16702             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16703             v.attr('colspan', function(i, val){
16704                 return parseInt(val) + 1;
16705             });
16706         });
16707                         
16708         
16709         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16710         
16711         this.setStartDate(this.startDate);
16712         this.setEndDate(this.endDate);
16713         
16714         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16715         
16716         this.fillDow();
16717         this.fillMonths();
16718         this.update();
16719         this.showMode();
16720         
16721         if(this.isInline) {
16722             this.show();
16723         }
16724     },
16725     
16726     picker : function()
16727     {
16728         return this.pickerEl;
16729 //        return this.el.select('.datepicker', true).first();
16730     },
16731     
16732     fillDow: function()
16733     {
16734         var dowCnt = this.weekStart;
16735         
16736         var dow = {
16737             tag: 'tr',
16738             cn: [
16739                 
16740             ]
16741         };
16742         
16743         if(this.calendarWeeks){
16744             dow.cn.push({
16745                 tag: 'th',
16746                 cls: 'cw',
16747                 html: '&nbsp;'
16748             })
16749         }
16750         
16751         while (dowCnt < this.weekStart + 7) {
16752             dow.cn.push({
16753                 tag: 'th',
16754                 cls: 'dow',
16755                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16756             });
16757         }
16758         
16759         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16760     },
16761     
16762     fillMonths: function()
16763     {    
16764         var i = 0;
16765         var months = this.picker().select('>.datepicker-months td', true).first();
16766         
16767         months.dom.innerHTML = '';
16768         
16769         while (i < 12) {
16770             var month = {
16771                 tag: 'span',
16772                 cls: 'month',
16773                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16774             };
16775             
16776             months.createChild(month);
16777         }
16778         
16779     },
16780     
16781     update: function()
16782     {
16783         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
16784         
16785         if (this.date < this.startDate) {
16786             this.viewDate = new Date(this.startDate);
16787         } else if (this.date > this.endDate) {
16788             this.viewDate = new Date(this.endDate);
16789         } else {
16790             this.viewDate = new Date(this.date);
16791         }
16792         
16793         this.fill();
16794     },
16795     
16796     fill: function() 
16797     {
16798         var d = new Date(this.viewDate),
16799                 year = d.getUTCFullYear(),
16800                 month = d.getUTCMonth(),
16801                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16802                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16803                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16804                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16805                 currentDate = this.date && this.date.valueOf(),
16806                 today = this.UTCToday();
16807         
16808         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16809         
16810 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16811         
16812 //        this.picker.select('>tfoot th.today').
16813 //                                              .text(dates[this.language].today)
16814 //                                              .toggle(this.todayBtn !== false);
16815     
16816         this.updateNavArrows();
16817         this.fillMonths();
16818                                                 
16819         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16820         
16821         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16822          
16823         prevMonth.setUTCDate(day);
16824         
16825         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16826         
16827         var nextMonth = new Date(prevMonth);
16828         
16829         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16830         
16831         nextMonth = nextMonth.valueOf();
16832         
16833         var fillMonths = false;
16834         
16835         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16836         
16837         while(prevMonth.valueOf() < nextMonth) {
16838             var clsName = '';
16839             
16840             if (prevMonth.getUTCDay() === this.weekStart) {
16841                 if(fillMonths){
16842                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16843                 }
16844                     
16845                 fillMonths = {
16846                     tag: 'tr',
16847                     cn: []
16848                 };
16849                 
16850                 if(this.calendarWeeks){
16851                     // ISO 8601: First week contains first thursday.
16852                     // ISO also states week starts on Monday, but we can be more abstract here.
16853                     var
16854                     // Start of current week: based on weekstart/current date
16855                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16856                     // Thursday of this week
16857                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16858                     // First Thursday of year, year from thursday
16859                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16860                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16861                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16862                     
16863                     fillMonths.cn.push({
16864                         tag: 'td',
16865                         cls: 'cw',
16866                         html: calWeek
16867                     });
16868                 }
16869             }
16870             
16871             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16872                 clsName += ' old';
16873             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16874                 clsName += ' new';
16875             }
16876             if (this.todayHighlight &&
16877                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16878                 prevMonth.getUTCMonth() == today.getMonth() &&
16879                 prevMonth.getUTCDate() == today.getDate()) {
16880                 clsName += ' today';
16881             }
16882             
16883             if (currentDate && prevMonth.valueOf() === currentDate) {
16884                 clsName += ' active';
16885             }
16886             
16887             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16888                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16889                     clsName += ' disabled';
16890             }
16891             
16892             fillMonths.cn.push({
16893                 tag: 'td',
16894                 cls: 'day ' + clsName,
16895                 html: prevMonth.getDate()
16896             });
16897             
16898             prevMonth.setDate(prevMonth.getDate()+1);
16899         }
16900           
16901         var currentYear = this.date && this.date.getUTCFullYear();
16902         var currentMonth = this.date && this.date.getUTCMonth();
16903         
16904         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16905         
16906         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16907             v.removeClass('active');
16908             
16909             if(currentYear === year && k === currentMonth){
16910                 v.addClass('active');
16911             }
16912             
16913             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16914                 v.addClass('disabled');
16915             }
16916             
16917         });
16918         
16919         
16920         year = parseInt(year/10, 10) * 10;
16921         
16922         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16923         
16924         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16925         
16926         year -= 1;
16927         for (var i = -1; i < 11; i++) {
16928             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16929                 tag: 'span',
16930                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16931                 html: year
16932             });
16933             
16934             year += 1;
16935         }
16936     },
16937     
16938     showMode: function(dir) 
16939     {
16940         if (dir) {
16941             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16942         }
16943         
16944         Roo.each(this.picker().select('>div',true).elements, function(v){
16945             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16946             v.hide();
16947         });
16948         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16949     },
16950     
16951     place: function()
16952     {
16953         if(this.isInline) {
16954             return;
16955         }
16956         
16957         this.picker().removeClass(['bottom', 'top']);
16958         
16959         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16960             /*
16961              * place to the top of element!
16962              *
16963              */
16964             
16965             this.picker().addClass('top');
16966             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16967             
16968             return;
16969         }
16970         
16971         this.picker().addClass('bottom');
16972         
16973         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16974     },
16975     
16976     parseDate : function(value)
16977     {
16978         if(!value || value instanceof Date){
16979             return value;
16980         }
16981         var v = Date.parseDate(value, this.format);
16982         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16983             v = Date.parseDate(value, 'Y-m-d');
16984         }
16985         if(!v && this.altFormats){
16986             if(!this.altFormatsArray){
16987                 this.altFormatsArray = this.altFormats.split("|");
16988             }
16989             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16990                 v = Date.parseDate(value, this.altFormatsArray[i]);
16991             }
16992         }
16993         return v;
16994     },
16995     
16996     formatDate : function(date, fmt)
16997     {   
16998         return (!date || !(date instanceof Date)) ?
16999         date : date.dateFormat(fmt || this.format);
17000     },
17001     
17002     onFocus : function()
17003     {
17004         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17005         this.show();
17006     },
17007     
17008     onBlur : function()
17009     {
17010         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17011         
17012         var d = this.inputEl().getValue();
17013         
17014         this.setValue(d);
17015                 
17016         this.hide();
17017     },
17018     
17019     show : function()
17020     {
17021         this.picker().show();
17022         this.update();
17023         this.place();
17024         
17025         this.fireEvent('show', this, this.date);
17026     },
17027     
17028     hide : function()
17029     {
17030         if(this.isInline) {
17031             return;
17032         }
17033         this.picker().hide();
17034         this.viewMode = this.startViewMode;
17035         this.showMode();
17036         
17037         this.fireEvent('hide', this, this.date);
17038         
17039     },
17040     
17041     onMousedown: function(e)
17042     {
17043         e.stopPropagation();
17044         e.preventDefault();
17045     },
17046     
17047     keyup: function(e)
17048     {
17049         Roo.bootstrap.DateField.superclass.keyup.call(this);
17050         this.update();
17051     },
17052
17053     setValue: function(v)
17054     {
17055         if(this.fireEvent('beforeselect', this, v) !== false){
17056             var d = new Date(this.parseDate(v) ).clearTime();
17057         
17058             if(isNaN(d.getTime())){
17059                 this.date = this.viewDate = '';
17060                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17061                 return;
17062             }
17063
17064             v = this.formatDate(d);
17065
17066             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17067
17068             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17069
17070             this.update();
17071
17072             this.fireEvent('select', this, this.date);
17073         }
17074     },
17075     
17076     getValue: function()
17077     {
17078         return this.formatDate(this.date);
17079     },
17080     
17081     fireKey: function(e)
17082     {
17083         if (!this.picker().isVisible()){
17084             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17085                 this.show();
17086             }
17087             return;
17088         }
17089         
17090         var dateChanged = false,
17091         dir, day, month,
17092         newDate, newViewDate;
17093         
17094         switch(e.keyCode){
17095             case 27: // escape
17096                 this.hide();
17097                 e.preventDefault();
17098                 break;
17099             case 37: // left
17100             case 39: // right
17101                 if (!this.keyboardNavigation) {
17102                     break;
17103                 }
17104                 dir = e.keyCode == 37 ? -1 : 1;
17105                 
17106                 if (e.ctrlKey){
17107                     newDate = this.moveYear(this.date, dir);
17108                     newViewDate = this.moveYear(this.viewDate, dir);
17109                 } else if (e.shiftKey){
17110                     newDate = this.moveMonth(this.date, dir);
17111                     newViewDate = this.moveMonth(this.viewDate, dir);
17112                 } else {
17113                     newDate = new Date(this.date);
17114                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17115                     newViewDate = new Date(this.viewDate);
17116                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17117                 }
17118                 if (this.dateWithinRange(newDate)){
17119                     this.date = newDate;
17120                     this.viewDate = newViewDate;
17121                     this.setValue(this.formatDate(this.date));
17122 //                    this.update();
17123                     e.preventDefault();
17124                     dateChanged = true;
17125                 }
17126                 break;
17127             case 38: // up
17128             case 40: // down
17129                 if (!this.keyboardNavigation) {
17130                     break;
17131                 }
17132                 dir = e.keyCode == 38 ? -1 : 1;
17133                 if (e.ctrlKey){
17134                     newDate = this.moveYear(this.date, dir);
17135                     newViewDate = this.moveYear(this.viewDate, dir);
17136                 } else if (e.shiftKey){
17137                     newDate = this.moveMonth(this.date, dir);
17138                     newViewDate = this.moveMonth(this.viewDate, dir);
17139                 } else {
17140                     newDate = new Date(this.date);
17141                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17142                     newViewDate = new Date(this.viewDate);
17143                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17144                 }
17145                 if (this.dateWithinRange(newDate)){
17146                     this.date = newDate;
17147                     this.viewDate = newViewDate;
17148                     this.setValue(this.formatDate(this.date));
17149 //                    this.update();
17150                     e.preventDefault();
17151                     dateChanged = true;
17152                 }
17153                 break;
17154             case 13: // enter
17155                 this.setValue(this.formatDate(this.date));
17156                 this.hide();
17157                 e.preventDefault();
17158                 break;
17159             case 9: // tab
17160                 this.setValue(this.formatDate(this.date));
17161                 this.hide();
17162                 break;
17163             case 16: // shift
17164             case 17: // ctrl
17165             case 18: // alt
17166                 break;
17167             default :
17168                 this.hide();
17169                 
17170         }
17171     },
17172     
17173     
17174     onClick: function(e) 
17175     {
17176         e.stopPropagation();
17177         e.preventDefault();
17178         
17179         var target = e.getTarget();
17180         
17181         if(target.nodeName.toLowerCase() === 'i'){
17182             target = Roo.get(target).dom.parentNode;
17183         }
17184         
17185         var nodeName = target.nodeName;
17186         var className = target.className;
17187         var html = target.innerHTML;
17188         //Roo.log(nodeName);
17189         
17190         switch(nodeName.toLowerCase()) {
17191             case 'th':
17192                 switch(className) {
17193                     case 'switch':
17194                         this.showMode(1);
17195                         break;
17196                     case 'prev':
17197                     case 'next':
17198                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17199                         switch(this.viewMode){
17200                                 case 0:
17201                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17202                                         break;
17203                                 case 1:
17204                                 case 2:
17205                                         this.viewDate = this.moveYear(this.viewDate, dir);
17206                                         break;
17207                         }
17208                         this.fill();
17209                         break;
17210                     case 'today':
17211                         var date = new Date();
17212                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17213 //                        this.fill()
17214                         this.setValue(this.formatDate(this.date));
17215                         
17216                         this.hide();
17217                         break;
17218                 }
17219                 break;
17220             case 'span':
17221                 if (className.indexOf('disabled') < 0) {
17222                     this.viewDate.setUTCDate(1);
17223                     if (className.indexOf('month') > -1) {
17224                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17225                     } else {
17226                         var year = parseInt(html, 10) || 0;
17227                         this.viewDate.setUTCFullYear(year);
17228                         
17229                     }
17230                     
17231                     if(this.singleMode){
17232                         this.setValue(this.formatDate(this.viewDate));
17233                         this.hide();
17234                         return;
17235                     }
17236                     
17237                     this.showMode(-1);
17238                     this.fill();
17239                 }
17240                 break;
17241                 
17242             case 'td':
17243                 //Roo.log(className);
17244                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17245                     var day = parseInt(html, 10) || 1;
17246                     var year = this.viewDate.getUTCFullYear(),
17247                         month = this.viewDate.getUTCMonth();
17248
17249                     if (className.indexOf('old') > -1) {
17250                         if(month === 0 ){
17251                             month = 11;
17252                             year -= 1;
17253                         }else{
17254                             month -= 1;
17255                         }
17256                     } else if (className.indexOf('new') > -1) {
17257                         if (month == 11) {
17258                             month = 0;
17259                             year += 1;
17260                         } else {
17261                             month += 1;
17262                         }
17263                     }
17264                     //Roo.log([year,month,day]);
17265                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17266                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17267 //                    this.fill();
17268                     //Roo.log(this.formatDate(this.date));
17269                     this.setValue(this.formatDate(this.date));
17270                     this.hide();
17271                 }
17272                 break;
17273         }
17274     },
17275     
17276     setStartDate: function(startDate)
17277     {
17278         this.startDate = startDate || -Infinity;
17279         if (this.startDate !== -Infinity) {
17280             this.startDate = this.parseDate(this.startDate);
17281         }
17282         this.update();
17283         this.updateNavArrows();
17284     },
17285
17286     setEndDate: function(endDate)
17287     {
17288         this.endDate = endDate || Infinity;
17289         if (this.endDate !== Infinity) {
17290             this.endDate = this.parseDate(this.endDate);
17291         }
17292         this.update();
17293         this.updateNavArrows();
17294     },
17295     
17296     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17297     {
17298         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17299         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17300             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17301         }
17302         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17303             return parseInt(d, 10);
17304         });
17305         this.update();
17306         this.updateNavArrows();
17307     },
17308     
17309     updateNavArrows: function() 
17310     {
17311         if(this.singleMode){
17312             return;
17313         }
17314         
17315         var d = new Date(this.viewDate),
17316         year = d.getUTCFullYear(),
17317         month = d.getUTCMonth();
17318         
17319         Roo.each(this.picker().select('.prev', true).elements, function(v){
17320             v.show();
17321             switch (this.viewMode) {
17322                 case 0:
17323
17324                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17325                         v.hide();
17326                     }
17327                     break;
17328                 case 1:
17329                 case 2:
17330                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17331                         v.hide();
17332                     }
17333                     break;
17334             }
17335         });
17336         
17337         Roo.each(this.picker().select('.next', true).elements, function(v){
17338             v.show();
17339             switch (this.viewMode) {
17340                 case 0:
17341
17342                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17343                         v.hide();
17344                     }
17345                     break;
17346                 case 1:
17347                 case 2:
17348                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17349                         v.hide();
17350                     }
17351                     break;
17352             }
17353         })
17354     },
17355     
17356     moveMonth: function(date, dir)
17357     {
17358         if (!dir) {
17359             return date;
17360         }
17361         var new_date = new Date(date.valueOf()),
17362         day = new_date.getUTCDate(),
17363         month = new_date.getUTCMonth(),
17364         mag = Math.abs(dir),
17365         new_month, test;
17366         dir = dir > 0 ? 1 : -1;
17367         if (mag == 1){
17368             test = dir == -1
17369             // If going back one month, make sure month is not current month
17370             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17371             ? function(){
17372                 return new_date.getUTCMonth() == month;
17373             }
17374             // If going forward one month, make sure month is as expected
17375             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17376             : function(){
17377                 return new_date.getUTCMonth() != new_month;
17378             };
17379             new_month = month + dir;
17380             new_date.setUTCMonth(new_month);
17381             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17382             if (new_month < 0 || new_month > 11) {
17383                 new_month = (new_month + 12) % 12;
17384             }
17385         } else {
17386             // For magnitudes >1, move one month at a time...
17387             for (var i=0; i<mag; i++) {
17388                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17389                 new_date = this.moveMonth(new_date, dir);
17390             }
17391             // ...then reset the day, keeping it in the new month
17392             new_month = new_date.getUTCMonth();
17393             new_date.setUTCDate(day);
17394             test = function(){
17395                 return new_month != new_date.getUTCMonth();
17396             };
17397         }
17398         // Common date-resetting loop -- if date is beyond end of month, make it
17399         // end of month
17400         while (test()){
17401             new_date.setUTCDate(--day);
17402             new_date.setUTCMonth(new_month);
17403         }
17404         return new_date;
17405     },
17406
17407     moveYear: function(date, dir)
17408     {
17409         return this.moveMonth(date, dir*12);
17410     },
17411
17412     dateWithinRange: function(date)
17413     {
17414         return date >= this.startDate && date <= this.endDate;
17415     },
17416
17417     
17418     remove: function() 
17419     {
17420         this.picker().remove();
17421     }
17422    
17423 });
17424
17425 Roo.apply(Roo.bootstrap.DateField,  {
17426     
17427     head : {
17428         tag: 'thead',
17429         cn: [
17430         {
17431             tag: 'tr',
17432             cn: [
17433             {
17434                 tag: 'th',
17435                 cls: 'prev',
17436                 html: '<i class="fa fa-arrow-left"/>'
17437             },
17438             {
17439                 tag: 'th',
17440                 cls: 'switch',
17441                 colspan: '5'
17442             },
17443             {
17444                 tag: 'th',
17445                 cls: 'next',
17446                 html: '<i class="fa fa-arrow-right"/>'
17447             }
17448
17449             ]
17450         }
17451         ]
17452     },
17453     
17454     content : {
17455         tag: 'tbody',
17456         cn: [
17457         {
17458             tag: 'tr',
17459             cn: [
17460             {
17461                 tag: 'td',
17462                 colspan: '7'
17463             }
17464             ]
17465         }
17466         ]
17467     },
17468     
17469     footer : {
17470         tag: 'tfoot',
17471         cn: [
17472         {
17473             tag: 'tr',
17474             cn: [
17475             {
17476                 tag: 'th',
17477                 colspan: '7',
17478                 cls: 'today'
17479             }
17480                     
17481             ]
17482         }
17483         ]
17484     },
17485     
17486     dates:{
17487         en: {
17488             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17489             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17490             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17491             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17492             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17493             today: "Today"
17494         }
17495     },
17496     
17497     modes: [
17498     {
17499         clsName: 'days',
17500         navFnc: 'Month',
17501         navStep: 1
17502     },
17503     {
17504         clsName: 'months',
17505         navFnc: 'FullYear',
17506         navStep: 1
17507     },
17508     {
17509         clsName: 'years',
17510         navFnc: 'FullYear',
17511         navStep: 10
17512     }]
17513 });
17514
17515 Roo.apply(Roo.bootstrap.DateField,  {
17516   
17517     template : {
17518         tag: 'div',
17519         cls: 'datepicker dropdown-menu roo-dynamic',
17520         cn: [
17521         {
17522             tag: 'div',
17523             cls: 'datepicker-days',
17524             cn: [
17525             {
17526                 tag: 'table',
17527                 cls: 'table-condensed',
17528                 cn:[
17529                 Roo.bootstrap.DateField.head,
17530                 {
17531                     tag: 'tbody'
17532                 },
17533                 Roo.bootstrap.DateField.footer
17534                 ]
17535             }
17536             ]
17537         },
17538         {
17539             tag: 'div',
17540             cls: 'datepicker-months',
17541             cn: [
17542             {
17543                 tag: 'table',
17544                 cls: 'table-condensed',
17545                 cn:[
17546                 Roo.bootstrap.DateField.head,
17547                 Roo.bootstrap.DateField.content,
17548                 Roo.bootstrap.DateField.footer
17549                 ]
17550             }
17551             ]
17552         },
17553         {
17554             tag: 'div',
17555             cls: 'datepicker-years',
17556             cn: [
17557             {
17558                 tag: 'table',
17559                 cls: 'table-condensed',
17560                 cn:[
17561                 Roo.bootstrap.DateField.head,
17562                 Roo.bootstrap.DateField.content,
17563                 Roo.bootstrap.DateField.footer
17564                 ]
17565             }
17566             ]
17567         }
17568         ]
17569     }
17570 });
17571
17572  
17573
17574  /*
17575  * - LGPL
17576  *
17577  * TimeField
17578  * 
17579  */
17580
17581 /**
17582  * @class Roo.bootstrap.TimeField
17583  * @extends Roo.bootstrap.Input
17584  * Bootstrap DateField class
17585  * 
17586  * 
17587  * @constructor
17588  * Create a new TimeField
17589  * @param {Object} config The config object
17590  */
17591
17592 Roo.bootstrap.TimeField = function(config){
17593     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17594     this.addEvents({
17595             /**
17596              * @event show
17597              * Fires when this field show.
17598              * @param {Roo.bootstrap.DateField} thisthis
17599              * @param {Mixed} date The date value
17600              */
17601             show : true,
17602             /**
17603              * @event show
17604              * Fires when this field hide.
17605              * @param {Roo.bootstrap.DateField} this
17606              * @param {Mixed} date The date value
17607              */
17608             hide : true,
17609             /**
17610              * @event select
17611              * Fires when select a date.
17612              * @param {Roo.bootstrap.DateField} this
17613              * @param {Mixed} date The date value
17614              */
17615             select : true
17616         });
17617 };
17618
17619 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17620     
17621     /**
17622      * @cfg {String} format
17623      * The default time format string which can be overriden for localization support.  The format must be
17624      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17625      */
17626     format : "H:i",
17627        
17628     onRender: function(ct, position)
17629     {
17630         
17631         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17632                 
17633         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17634         
17635         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17636         
17637         this.pop = this.picker().select('>.datepicker-time',true).first();
17638         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17639         
17640         this.picker().on('mousedown', this.onMousedown, this);
17641         this.picker().on('click', this.onClick, this);
17642         
17643         this.picker().addClass('datepicker-dropdown');
17644     
17645         this.fillTime();
17646         this.update();
17647             
17648         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17649         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17650         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17651         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17652         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17653         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17654
17655     },
17656     
17657     fireKey: function(e){
17658         if (!this.picker().isVisible()){
17659             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17660                 this.show();
17661             }
17662             return;
17663         }
17664
17665         e.preventDefault();
17666         
17667         switch(e.keyCode){
17668             case 27: // escape
17669                 this.hide();
17670                 break;
17671             case 37: // left
17672             case 39: // right
17673                 this.onTogglePeriod();
17674                 break;
17675             case 38: // up
17676                 this.onIncrementMinutes();
17677                 break;
17678             case 40: // down
17679                 this.onDecrementMinutes();
17680                 break;
17681             case 13: // enter
17682             case 9: // tab
17683                 this.setTime();
17684                 break;
17685         }
17686     },
17687     
17688     onClick: function(e) {
17689         e.stopPropagation();
17690         e.preventDefault();
17691     },
17692     
17693     picker : function()
17694     {
17695         return this.el.select('.datepicker', true).first();
17696     },
17697     
17698     fillTime: function()
17699     {    
17700         var time = this.pop.select('tbody', true).first();
17701         
17702         time.dom.innerHTML = '';
17703         
17704         time.createChild({
17705             tag: 'tr',
17706             cn: [
17707                 {
17708                     tag: 'td',
17709                     cn: [
17710                         {
17711                             tag: 'a',
17712                             href: '#',
17713                             cls: 'btn',
17714                             cn: [
17715                                 {
17716                                     tag: 'span',
17717                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17718                                 }
17719                             ]
17720                         } 
17721                     ]
17722                 },
17723                 {
17724                     tag: 'td',
17725                     cls: 'separator'
17726                 },
17727                 {
17728                     tag: 'td',
17729                     cn: [
17730                         {
17731                             tag: 'a',
17732                             href: '#',
17733                             cls: 'btn',
17734                             cn: [
17735                                 {
17736                                     tag: 'span',
17737                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17738                                 }
17739                             ]
17740                         }
17741                     ]
17742                 },
17743                 {
17744                     tag: 'td',
17745                     cls: 'separator'
17746                 }
17747             ]
17748         });
17749         
17750         time.createChild({
17751             tag: 'tr',
17752             cn: [
17753                 {
17754                     tag: 'td',
17755                     cn: [
17756                         {
17757                             tag: 'span',
17758                             cls: 'timepicker-hour',
17759                             html: '00'
17760                         }  
17761                     ]
17762                 },
17763                 {
17764                     tag: 'td',
17765                     cls: 'separator',
17766                     html: ':'
17767                 },
17768                 {
17769                     tag: 'td',
17770                     cn: [
17771                         {
17772                             tag: 'span',
17773                             cls: 'timepicker-minute',
17774                             html: '00'
17775                         }  
17776                     ]
17777                 },
17778                 {
17779                     tag: 'td',
17780                     cls: 'separator'
17781                 },
17782                 {
17783                     tag: 'td',
17784                     cn: [
17785                         {
17786                             tag: 'button',
17787                             type: 'button',
17788                             cls: 'btn btn-primary period',
17789                             html: 'AM'
17790                             
17791                         }
17792                     ]
17793                 }
17794             ]
17795         });
17796         
17797         time.createChild({
17798             tag: 'tr',
17799             cn: [
17800                 {
17801                     tag: 'td',
17802                     cn: [
17803                         {
17804                             tag: 'a',
17805                             href: '#',
17806                             cls: 'btn',
17807                             cn: [
17808                                 {
17809                                     tag: 'span',
17810                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17811                                 }
17812                             ]
17813                         }
17814                     ]
17815                 },
17816                 {
17817                     tag: 'td',
17818                     cls: 'separator'
17819                 },
17820                 {
17821                     tag: 'td',
17822                     cn: [
17823                         {
17824                             tag: 'a',
17825                             href: '#',
17826                             cls: 'btn',
17827                             cn: [
17828                                 {
17829                                     tag: 'span',
17830                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17831                                 }
17832                             ]
17833                         }
17834                     ]
17835                 },
17836                 {
17837                     tag: 'td',
17838                     cls: 'separator'
17839                 }
17840             ]
17841         });
17842         
17843     },
17844     
17845     update: function()
17846     {
17847         
17848         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17849         
17850         this.fill();
17851     },
17852     
17853     fill: function() 
17854     {
17855         var hours = this.time.getHours();
17856         var minutes = this.time.getMinutes();
17857         var period = 'AM';
17858         
17859         if(hours > 11){
17860             period = 'PM';
17861         }
17862         
17863         if(hours == 0){
17864             hours = 12;
17865         }
17866         
17867         
17868         if(hours > 12){
17869             hours = hours - 12;
17870         }
17871         
17872         if(hours < 10){
17873             hours = '0' + hours;
17874         }
17875         
17876         if(minutes < 10){
17877             minutes = '0' + minutes;
17878         }
17879         
17880         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17881         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17882         this.pop.select('button', true).first().dom.innerHTML = period;
17883         
17884     },
17885     
17886     place: function()
17887     {   
17888         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17889         
17890         var cls = ['bottom'];
17891         
17892         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17893             cls.pop();
17894             cls.push('top');
17895         }
17896         
17897         cls.push('right');
17898         
17899         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17900             cls.pop();
17901             cls.push('left');
17902         }
17903         
17904         this.picker().addClass(cls.join('-'));
17905         
17906         var _this = this;
17907         
17908         Roo.each(cls, function(c){
17909             if(c == 'bottom'){
17910                 _this.picker().setTop(_this.inputEl().getHeight());
17911                 return;
17912             }
17913             if(c == 'top'){
17914                 _this.picker().setTop(0 - _this.picker().getHeight());
17915                 return;
17916             }
17917             
17918             if(c == 'left'){
17919                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17920                 return;
17921             }
17922             if(c == 'right'){
17923                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17924                 return;
17925             }
17926         });
17927         
17928     },
17929   
17930     onFocus : function()
17931     {
17932         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17933         this.show();
17934     },
17935     
17936     onBlur : function()
17937     {
17938         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17939         this.hide();
17940     },
17941     
17942     show : function()
17943     {
17944         this.picker().show();
17945         this.pop.show();
17946         this.update();
17947         this.place();
17948         
17949         this.fireEvent('show', this, this.date);
17950     },
17951     
17952     hide : function()
17953     {
17954         this.picker().hide();
17955         this.pop.hide();
17956         
17957         this.fireEvent('hide', this, this.date);
17958     },
17959     
17960     setTime : function()
17961     {
17962         this.hide();
17963         this.setValue(this.time.format(this.format));
17964         
17965         this.fireEvent('select', this, this.date);
17966         
17967         
17968     },
17969     
17970     onMousedown: function(e){
17971         e.stopPropagation();
17972         e.preventDefault();
17973     },
17974     
17975     onIncrementHours: function()
17976     {
17977         Roo.log('onIncrementHours');
17978         this.time = this.time.add(Date.HOUR, 1);
17979         this.update();
17980         
17981     },
17982     
17983     onDecrementHours: function()
17984     {
17985         Roo.log('onDecrementHours');
17986         this.time = this.time.add(Date.HOUR, -1);
17987         this.update();
17988     },
17989     
17990     onIncrementMinutes: function()
17991     {
17992         Roo.log('onIncrementMinutes');
17993         this.time = this.time.add(Date.MINUTE, 1);
17994         this.update();
17995     },
17996     
17997     onDecrementMinutes: function()
17998     {
17999         Roo.log('onDecrementMinutes');
18000         this.time = this.time.add(Date.MINUTE, -1);
18001         this.update();
18002     },
18003     
18004     onTogglePeriod: function()
18005     {
18006         Roo.log('onTogglePeriod');
18007         this.time = this.time.add(Date.HOUR, 12);
18008         this.update();
18009     }
18010     
18011    
18012 });
18013
18014 Roo.apply(Roo.bootstrap.TimeField,  {
18015     
18016     content : {
18017         tag: 'tbody',
18018         cn: [
18019             {
18020                 tag: 'tr',
18021                 cn: [
18022                 {
18023                     tag: 'td',
18024                     colspan: '7'
18025                 }
18026                 ]
18027             }
18028         ]
18029     },
18030     
18031     footer : {
18032         tag: 'tfoot',
18033         cn: [
18034             {
18035                 tag: 'tr',
18036                 cn: [
18037                 {
18038                     tag: 'th',
18039                     colspan: '7',
18040                     cls: '',
18041                     cn: [
18042                         {
18043                             tag: 'button',
18044                             cls: 'btn btn-info ok',
18045                             html: 'OK'
18046                         }
18047                     ]
18048                 }
18049
18050                 ]
18051             }
18052         ]
18053     }
18054 });
18055
18056 Roo.apply(Roo.bootstrap.TimeField,  {
18057   
18058     template : {
18059         tag: 'div',
18060         cls: 'datepicker dropdown-menu',
18061         cn: [
18062             {
18063                 tag: 'div',
18064                 cls: 'datepicker-time',
18065                 cn: [
18066                 {
18067                     tag: 'table',
18068                     cls: 'table-condensed',
18069                     cn:[
18070                     Roo.bootstrap.TimeField.content,
18071                     Roo.bootstrap.TimeField.footer
18072                     ]
18073                 }
18074                 ]
18075             }
18076         ]
18077     }
18078 });
18079
18080  
18081
18082  /*
18083  * - LGPL
18084  *
18085  * MonthField
18086  * 
18087  */
18088
18089 /**
18090  * @class Roo.bootstrap.MonthField
18091  * @extends Roo.bootstrap.Input
18092  * Bootstrap MonthField class
18093  * 
18094  * @cfg {String} language default en
18095  * 
18096  * @constructor
18097  * Create a new MonthField
18098  * @param {Object} config The config object
18099  */
18100
18101 Roo.bootstrap.MonthField = function(config){
18102     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18103     
18104     this.addEvents({
18105         /**
18106          * @event show
18107          * Fires when this field show.
18108          * @param {Roo.bootstrap.MonthField} this
18109          * @param {Mixed} date The date value
18110          */
18111         show : true,
18112         /**
18113          * @event show
18114          * Fires when this field hide.
18115          * @param {Roo.bootstrap.MonthField} this
18116          * @param {Mixed} date The date value
18117          */
18118         hide : true,
18119         /**
18120          * @event select
18121          * Fires when select a date.
18122          * @param {Roo.bootstrap.MonthField} this
18123          * @param {String} oldvalue The old value
18124          * @param {String} newvalue The new value
18125          */
18126         select : true
18127     });
18128 };
18129
18130 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18131     
18132     onRender: function(ct, position)
18133     {
18134         
18135         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18136         
18137         this.language = this.language || 'en';
18138         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18139         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18140         
18141         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18142         this.isInline = false;
18143         this.isInput = true;
18144         this.component = this.el.select('.add-on', true).first() || false;
18145         this.component = (this.component && this.component.length === 0) ? false : this.component;
18146         this.hasInput = this.component && this.inputEL().length;
18147         
18148         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18149         
18150         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18151         
18152         this.picker().on('mousedown', this.onMousedown, this);
18153         this.picker().on('click', this.onClick, this);
18154         
18155         this.picker().addClass('datepicker-dropdown');
18156         
18157         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18158             v.setStyle('width', '189px');
18159         });
18160         
18161         this.fillMonths();
18162         
18163         this.update();
18164         
18165         if(this.isInline) {
18166             this.show();
18167         }
18168         
18169     },
18170     
18171     setValue: function(v, suppressEvent)
18172     {   
18173         var o = this.getValue();
18174         
18175         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18176         
18177         this.update();
18178
18179         if(suppressEvent !== true){
18180             this.fireEvent('select', this, o, v);
18181         }
18182         
18183     },
18184     
18185     getValue: function()
18186     {
18187         return this.value;
18188     },
18189     
18190     onClick: function(e) 
18191     {
18192         e.stopPropagation();
18193         e.preventDefault();
18194         
18195         var target = e.getTarget();
18196         
18197         if(target.nodeName.toLowerCase() === 'i'){
18198             target = Roo.get(target).dom.parentNode;
18199         }
18200         
18201         var nodeName = target.nodeName;
18202         var className = target.className;
18203         var html = target.innerHTML;
18204         
18205         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18206             return;
18207         }
18208         
18209         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18210         
18211         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18212         
18213         this.hide();
18214                         
18215     },
18216     
18217     picker : function()
18218     {
18219         return this.pickerEl;
18220     },
18221     
18222     fillMonths: function()
18223     {    
18224         var i = 0;
18225         var months = this.picker().select('>.datepicker-months td', true).first();
18226         
18227         months.dom.innerHTML = '';
18228         
18229         while (i < 12) {
18230             var month = {
18231                 tag: 'span',
18232                 cls: 'month',
18233                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18234             };
18235             
18236             months.createChild(month);
18237         }
18238         
18239     },
18240     
18241     update: function()
18242     {
18243         var _this = this;
18244         
18245         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18246             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18247         }
18248         
18249         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18250             e.removeClass('active');
18251             
18252             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18253                 e.addClass('active');
18254             }
18255         })
18256     },
18257     
18258     place: function()
18259     {
18260         if(this.isInline) {
18261             return;
18262         }
18263         
18264         this.picker().removeClass(['bottom', 'top']);
18265         
18266         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18267             /*
18268              * place to the top of element!
18269              *
18270              */
18271             
18272             this.picker().addClass('top');
18273             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18274             
18275             return;
18276         }
18277         
18278         this.picker().addClass('bottom');
18279         
18280         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18281     },
18282     
18283     onFocus : function()
18284     {
18285         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18286         this.show();
18287     },
18288     
18289     onBlur : function()
18290     {
18291         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18292         
18293         var d = this.inputEl().getValue();
18294         
18295         this.setValue(d);
18296                 
18297         this.hide();
18298     },
18299     
18300     show : function()
18301     {
18302         this.picker().show();
18303         this.picker().select('>.datepicker-months', true).first().show();
18304         this.update();
18305         this.place();
18306         
18307         this.fireEvent('show', this, this.date);
18308     },
18309     
18310     hide : function()
18311     {
18312         if(this.isInline) {
18313             return;
18314         }
18315         this.picker().hide();
18316         this.fireEvent('hide', this, this.date);
18317         
18318     },
18319     
18320     onMousedown: function(e)
18321     {
18322         e.stopPropagation();
18323         e.preventDefault();
18324     },
18325     
18326     keyup: function(e)
18327     {
18328         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18329         this.update();
18330     },
18331
18332     fireKey: function(e)
18333     {
18334         if (!this.picker().isVisible()){
18335             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18336                 this.show();
18337             }
18338             return;
18339         }
18340         
18341         var dir;
18342         
18343         switch(e.keyCode){
18344             case 27: // escape
18345                 this.hide();
18346                 e.preventDefault();
18347                 break;
18348             case 37: // left
18349             case 39: // right
18350                 dir = e.keyCode == 37 ? -1 : 1;
18351                 
18352                 this.vIndex = this.vIndex + dir;
18353                 
18354                 if(this.vIndex < 0){
18355                     this.vIndex = 0;
18356                 }
18357                 
18358                 if(this.vIndex > 11){
18359                     this.vIndex = 11;
18360                 }
18361                 
18362                 if(isNaN(this.vIndex)){
18363                     this.vIndex = 0;
18364                 }
18365                 
18366                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18367                 
18368                 break;
18369             case 38: // up
18370             case 40: // down
18371                 
18372                 dir = e.keyCode == 38 ? -1 : 1;
18373                 
18374                 this.vIndex = this.vIndex + dir * 4;
18375                 
18376                 if(this.vIndex < 0){
18377                     this.vIndex = 0;
18378                 }
18379                 
18380                 if(this.vIndex > 11){
18381                     this.vIndex = 11;
18382                 }
18383                 
18384                 if(isNaN(this.vIndex)){
18385                     this.vIndex = 0;
18386                 }
18387                 
18388                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18389                 break;
18390                 
18391             case 13: // enter
18392                 
18393                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18394                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18395                 }
18396                 
18397                 this.hide();
18398                 e.preventDefault();
18399                 break;
18400             case 9: // tab
18401                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18402                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18403                 }
18404                 this.hide();
18405                 break;
18406             case 16: // shift
18407             case 17: // ctrl
18408             case 18: // alt
18409                 break;
18410             default :
18411                 this.hide();
18412                 
18413         }
18414     },
18415     
18416     remove: function() 
18417     {
18418         this.picker().remove();
18419     }
18420    
18421 });
18422
18423 Roo.apply(Roo.bootstrap.MonthField,  {
18424     
18425     content : {
18426         tag: 'tbody',
18427         cn: [
18428         {
18429             tag: 'tr',
18430             cn: [
18431             {
18432                 tag: 'td',
18433                 colspan: '7'
18434             }
18435             ]
18436         }
18437         ]
18438     },
18439     
18440     dates:{
18441         en: {
18442             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18443             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18444         }
18445     }
18446 });
18447
18448 Roo.apply(Roo.bootstrap.MonthField,  {
18449   
18450     template : {
18451         tag: 'div',
18452         cls: 'datepicker dropdown-menu roo-dynamic',
18453         cn: [
18454             {
18455                 tag: 'div',
18456                 cls: 'datepicker-months',
18457                 cn: [
18458                 {
18459                     tag: 'table',
18460                     cls: 'table-condensed',
18461                     cn:[
18462                         Roo.bootstrap.DateField.content
18463                     ]
18464                 }
18465                 ]
18466             }
18467         ]
18468     }
18469 });
18470
18471  
18472
18473  
18474  /*
18475  * - LGPL
18476  *
18477  * CheckBox
18478  * 
18479  */
18480
18481 /**
18482  * @class Roo.bootstrap.CheckBox
18483  * @extends Roo.bootstrap.Input
18484  * Bootstrap CheckBox class
18485  * 
18486  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18487  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18488  * @cfg {String} boxLabel The text that appears beside the checkbox
18489  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18490  * @cfg {Boolean} checked initnal the element
18491  * @cfg {Boolean} inline inline the element (default false)
18492  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18493  * 
18494  * @constructor
18495  * Create a new CheckBox
18496  * @param {Object} config The config object
18497  */
18498
18499 Roo.bootstrap.CheckBox = function(config){
18500     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18501    
18502     this.addEvents({
18503         /**
18504         * @event check
18505         * Fires when the element is checked or unchecked.
18506         * @param {Roo.bootstrap.CheckBox} this This input
18507         * @param {Boolean} checked The new checked value
18508         */
18509        check : true
18510     });
18511     
18512 };
18513
18514 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18515   
18516     inputType: 'checkbox',
18517     inputValue: 1,
18518     valueOff: 0,
18519     boxLabel: false,
18520     checked: false,
18521     weight : false,
18522     inline: false,
18523     
18524     getAutoCreate : function()
18525     {
18526         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18527         
18528         var id = Roo.id();
18529         
18530         var cfg = {};
18531         
18532         cfg.cls = 'form-group ' + this.inputType; //input-group
18533         
18534         if(this.inline){
18535             cfg.cls += ' ' + this.inputType + '-inline';
18536         }
18537         
18538         var input =  {
18539             tag: 'input',
18540             id : id,
18541             type : this.inputType,
18542             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18543             cls : 'roo-' + this.inputType, //'form-box',
18544             placeholder : this.placeholder || ''
18545             
18546         };
18547         
18548         if (this.weight) { // Validity check?
18549             cfg.cls += " " + this.inputType + "-" + this.weight;
18550         }
18551         
18552         if (this.disabled) {
18553             input.disabled=true;
18554         }
18555         
18556         if(this.checked){
18557             input.checked = this.checked;
18558         }
18559         
18560         if (this.name) {
18561             input.name = this.name;
18562         }
18563         
18564         if (this.size) {
18565             input.cls += ' input-' + this.size;
18566         }
18567         
18568         var settings=this;
18569         
18570         ['xs','sm','md','lg'].map(function(size){
18571             if (settings[size]) {
18572                 cfg.cls += ' col-' + size + '-' + settings[size];
18573             }
18574         });
18575         
18576         var inputblock = input;
18577          
18578         if (this.before || this.after) {
18579             
18580             inputblock = {
18581                 cls : 'input-group',
18582                 cn :  [] 
18583             };
18584             
18585             if (this.before) {
18586                 inputblock.cn.push({
18587                     tag :'span',
18588                     cls : 'input-group-addon',
18589                     html : this.before
18590                 });
18591             }
18592             
18593             inputblock.cn.push(input);
18594             
18595             if (this.after) {
18596                 inputblock.cn.push({
18597                     tag :'span',
18598                     cls : 'input-group-addon',
18599                     html : this.after
18600                 });
18601             }
18602             
18603         }
18604         
18605         if (align ==='left' && this.fieldLabel.length) {
18606 //                Roo.log("left and has label");
18607                 cfg.cn = [
18608                     
18609                     {
18610                         tag: 'label',
18611                         'for' :  id,
18612                         cls : 'control-label col-md-' + this.labelWidth,
18613                         html : this.fieldLabel
18614                         
18615                     },
18616                     {
18617                         cls : "col-md-" + (12 - this.labelWidth), 
18618                         cn: [
18619                             inputblock
18620                         ]
18621                     }
18622                     
18623                 ];
18624         } else if ( this.fieldLabel.length) {
18625 //                Roo.log(" label");
18626                 cfg.cn = [
18627                    
18628                     {
18629                         tag: this.boxLabel ? 'span' : 'label',
18630                         'for': id,
18631                         cls: 'control-label box-input-label',
18632                         //cls : 'input-group-addon',
18633                         html : this.fieldLabel
18634                         
18635                     },
18636                     
18637                     inputblock
18638                     
18639                 ];
18640
18641         } else {
18642             
18643 //                Roo.log(" no label && no align");
18644                 cfg.cn = [  inputblock ] ;
18645                 
18646                 
18647         }
18648         if(this.boxLabel){
18649              var boxLabelCfg = {
18650                 tag: 'label',
18651                 //'for': id, // box label is handled by onclick - so no for...
18652                 cls: 'box-label',
18653                 html: this.boxLabel
18654             };
18655             
18656             if(this.tooltip){
18657                 boxLabelCfg.tooltip = this.tooltip;
18658             }
18659              
18660             cfg.cn.push(boxLabelCfg);
18661         }
18662         
18663         
18664        
18665         return cfg;
18666         
18667     },
18668     
18669     /**
18670      * return the real input element.
18671      */
18672     inputEl: function ()
18673     {
18674         return this.el.select('input.roo-' + this.inputType,true).first();
18675     },
18676     
18677     labelEl: function()
18678     {
18679         return this.el.select('label.control-label',true).first();
18680     },
18681     /* depricated... */
18682     
18683     label: function()
18684     {
18685         return this.labelEl();
18686     },
18687     
18688     initEvents : function()
18689     {
18690 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18691         
18692         this.inputEl().on('click', this.onClick,  this);
18693         
18694         if (this.boxLabel) { 
18695             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18696         }
18697         
18698         this.startValue = this.getValue();
18699         
18700         if(this.groupId){
18701             Roo.bootstrap.CheckBox.register(this);
18702         }
18703     },
18704     
18705     onClick : function()
18706     {   
18707         this.setChecked(!this.checked);
18708     },
18709     
18710     setChecked : function(state,suppressEvent)
18711     {
18712         this.startValue = this.getValue();
18713         
18714         if(this.inputType == 'radio'){
18715             
18716             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18717                 e.dom.checked = false;
18718             });
18719             
18720             this.inputEl().dom.checked = true;
18721             
18722             this.inputEl().dom.value = this.inputValue;
18723             
18724             if(suppressEvent !== true){
18725                 this.fireEvent('check', this, true);
18726             }
18727             
18728             this.validate();
18729             
18730             return;
18731         }
18732         
18733         this.checked = state;
18734         
18735         this.inputEl().dom.checked = state;
18736         
18737         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18738         
18739         if(suppressEvent !== true){
18740             this.fireEvent('check', this, state);
18741         }
18742         
18743         this.validate();
18744     },
18745     
18746     getValue : function()
18747     {
18748         if(this.inputType == 'radio'){
18749             return this.getGroupValue();
18750         }
18751         
18752         return this.inputEl().getValue();
18753         
18754     },
18755     
18756     getGroupValue : function()
18757     {
18758         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18759             return '';
18760         }
18761         
18762         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18763     },
18764     
18765     setValue : function(v,suppressEvent)
18766     {
18767         if(this.inputType == 'radio'){
18768             this.setGroupValue(v, suppressEvent);
18769             return;
18770         }
18771         
18772         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18773         
18774         this.validate();
18775     },
18776     
18777     setGroupValue : function(v, suppressEvent)
18778     {
18779         this.startValue = this.getValue();
18780         
18781         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18782             e.dom.checked = false;
18783             
18784             if(e.dom.value == v){
18785                 e.dom.checked = true;
18786             }
18787         });
18788         
18789         if(suppressEvent !== true){
18790             this.fireEvent('check', this, true);
18791         }
18792
18793         this.validate();
18794         
18795         return;
18796     },
18797     
18798     validate : function()
18799     {
18800         if(
18801                 this.disabled || 
18802                 (this.inputType == 'radio' && this.validateRadio()) ||
18803                 (this.inputType == 'checkbox' && this.validateCheckbox())
18804         ){
18805             this.markValid();
18806             return true;
18807         }
18808         
18809         this.markInvalid();
18810         return false;
18811     },
18812     
18813     validateRadio : function()
18814     {
18815         var valid = false;
18816         
18817         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18818             if(!e.dom.checked){
18819                 return;
18820             }
18821             
18822             valid = true;
18823             
18824             return false;
18825         });
18826         
18827         return valid;
18828     },
18829     
18830     validateCheckbox : function()
18831     {
18832         if(!this.groupId){
18833             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18834         }
18835         
18836         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18837         
18838         if(!group){
18839             return false;
18840         }
18841         
18842         var r = false;
18843         
18844         for(var i in group){
18845             if(r){
18846                 break;
18847             }
18848             
18849             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18850         }
18851         
18852         return r;
18853     },
18854     
18855     /**
18856      * Mark this field as valid
18857      */
18858     markValid : function()
18859     {
18860         if(this.allowBlank){
18861             return;
18862         }
18863         
18864         var _this = this;
18865         
18866         this.fireEvent('valid', this);
18867         
18868         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18869         
18870         if(this.groupId){
18871             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18872         }
18873         
18874         if(label){
18875             label.markValid();
18876         }
18877         
18878         if(this.inputType == 'radio'){
18879             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18880                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18881                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18882             });
18883             
18884             return;
18885         }
18886         
18887         if(!this.groupId){
18888             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18889             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18890             return;
18891         }
18892         
18893         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18894             
18895         if(!group){
18896             return;
18897         }
18898         
18899         for(var i in group){
18900             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18901             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18902         }
18903     },
18904     
18905      /**
18906      * Mark this field as invalid
18907      * @param {String} msg The validation message
18908      */
18909     markInvalid : function(msg)
18910     {
18911         if(this.allowBlank){
18912             return;
18913         }
18914         
18915         var _this = this;
18916         
18917         this.fireEvent('invalid', this, msg);
18918         
18919         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18920         
18921         if(this.groupId){
18922             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18923         }
18924         
18925         if(label){
18926             label.markInvalid();
18927         }
18928             
18929         if(this.inputType == 'radio'){
18930             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18931                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18932                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18933             });
18934             
18935             return;
18936         }
18937         
18938         if(!this.groupId){
18939             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18940             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18941             return;
18942         }
18943         
18944         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18945         
18946         if(!group){
18947             return;
18948         }
18949         
18950         for(var i in group){
18951             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18952             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18953         }
18954         
18955     }
18956     
18957 });
18958
18959 Roo.apply(Roo.bootstrap.CheckBox, {
18960     
18961     groups: {},
18962     
18963      /**
18964     * register a CheckBox Group
18965     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18966     */
18967     register : function(checkbox)
18968     {
18969         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18970             this.groups[checkbox.groupId] = {};
18971         }
18972         
18973         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18974             return;
18975         }
18976         
18977         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18978         
18979     },
18980     /**
18981     * fetch a CheckBox Group based on the group ID
18982     * @param {string} the group ID
18983     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18984     */
18985     get: function(groupId) {
18986         if (typeof(this.groups[groupId]) == 'undefined') {
18987             return false;
18988         }
18989         
18990         return this.groups[groupId] ;
18991     }
18992     
18993     
18994 });
18995 /*
18996  * - LGPL
18997  *
18998  * Radio
18999  *
19000  *
19001  * not inline
19002  *<div class="radio">
19003   <label>
19004     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19005     Option one is this and that&mdash;be sure to include why it's great
19006   </label>
19007 </div>
19008  *
19009  *
19010  *inline
19011  *<span>
19012  *<label class="radio-inline">fieldLabel</label>
19013  *<label class="radio-inline">
19014   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19015 </label>
19016 <span>
19017  * 
19018  * 
19019  */
19020
19021 /**
19022  * @class Roo.bootstrap.Radio
19023  * @extends Roo.bootstrap.CheckBox
19024  * Bootstrap Radio class
19025
19026  * @constructor
19027  * Create a new Radio
19028  * @param {Object} config The config object
19029  */
19030
19031 Roo.bootstrap.Radio = function(config){
19032     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19033    
19034 };
19035
19036 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19037     
19038     inputType: 'radio',
19039     inputValue: '',
19040     valueOff: '',
19041     
19042     getAutoCreate : function()
19043     {
19044         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19045         align = align || 'left'; // default...
19046         
19047         
19048         
19049         var id = Roo.id();
19050         
19051         var cfg = {
19052                 tag : this.inline ? 'span' : 'div',
19053                 cls : '',
19054                 cn : []
19055         };
19056         
19057         var inline = this.inline ? ' radio-inline' : '';
19058         
19059         var lbl = {
19060                 tag: 'label' ,
19061                 // does not need for, as we wrap the input with it..
19062                 'for' : id,
19063                 cls : 'control-label box-label' + inline,
19064                 cn : []
19065         };
19066         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19067         
19068         var fieldLabel = {
19069             tag: 'label' ,
19070             //cls : 'control-label' + inline,
19071             html : this.fieldLabel,
19072             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19073         };
19074         
19075  
19076         
19077         
19078         var input =  {
19079             tag: 'input',
19080             id : id,
19081             type : this.inputType,
19082             //value : (!this.checked) ? this.valueOff : this.inputValue,
19083             value : this.inputValue,
19084             cls : 'roo-radio',
19085             placeholder : this.placeholder || '' // ?? needed????
19086             
19087         };
19088         if (this.weight) { // Validity check?
19089             input.cls += " radio-" + this.weight;
19090         }
19091         if (this.disabled) {
19092             input.disabled=true;
19093         }
19094         
19095         if(this.checked){
19096             input.checked = this.checked;
19097         }
19098         
19099         if (this.name) {
19100             input.name = this.name;
19101         }
19102         
19103         if (this.size) {
19104             input.cls += ' input-' + this.size;
19105         }
19106         
19107         //?? can span's inline have a width??
19108         
19109         var settings=this;
19110         ['xs','sm','md','lg'].map(function(size){
19111             if (settings[size]) {
19112                 cfg.cls += ' col-' + size + '-' + settings[size];
19113             }
19114         });
19115         
19116         var inputblock = input;
19117         
19118         if (this.before || this.after) {
19119             
19120             inputblock = {
19121                 cls : 'input-group',
19122                 tag : 'span',
19123                 cn :  [] 
19124             };
19125             if (this.before) {
19126                 inputblock.cn.push({
19127                     tag :'span',
19128                     cls : 'input-group-addon',
19129                     html : this.before
19130                 });
19131             }
19132             inputblock.cn.push(input);
19133             if (this.after) {
19134                 inputblock.cn.push({
19135                     tag :'span',
19136                     cls : 'input-group-addon',
19137                     html : this.after
19138                 });
19139             }
19140             
19141         };
19142         
19143         
19144         if (this.fieldLabel && this.fieldLabel.length) {
19145             cfg.cn.push(fieldLabel);
19146         }
19147        
19148         // normal bootstrap puts the input inside the label.
19149         // however with our styled version - it has to go after the input.
19150        
19151         //lbl.cn.push(inputblock);
19152         
19153         var lblwrap =  {
19154             tag: 'span',
19155             cls: 'radio' + inline,
19156             cn: [
19157                 inputblock,
19158                 lbl
19159             ]
19160         };
19161         
19162         cfg.cn.push( lblwrap);
19163         
19164         if(this.boxLabel){
19165             lbl.cn.push({
19166                 tag: 'span',
19167                 html: this.boxLabel
19168             })
19169         }
19170          
19171         
19172         return cfg;
19173         
19174     },
19175     
19176     initEvents : function()
19177     {
19178 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19179         
19180         this.inputEl().on('click', this.onClick,  this);
19181         if (this.boxLabel) {
19182             //Roo.log('find label');
19183             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19184         }
19185         
19186     },
19187     
19188     inputEl: function ()
19189     {
19190         return this.el.select('input.roo-radio',true).first();
19191     },
19192     onClick : function()
19193     {   
19194         Roo.log("click");
19195         this.setChecked(true);
19196     },
19197     
19198     setChecked : function(state,suppressEvent)
19199     {
19200         if(state){
19201             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19202                 v.dom.checked = false;
19203             });
19204         }
19205         Roo.log(this.inputEl().dom);
19206         this.checked = state;
19207         this.inputEl().dom.checked = state;
19208         
19209         if(suppressEvent !== true){
19210             this.fireEvent('check', this, state);
19211         }
19212         
19213         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19214         
19215     },
19216     
19217     getGroupValue : function()
19218     {
19219         var value = '';
19220         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19221             if(v.dom.checked == true){
19222                 value = v.dom.value;
19223             }
19224         });
19225         
19226         return value;
19227     },
19228     
19229     /**
19230      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19231      * @return {Mixed} value The field value
19232      */
19233     getValue : function(){
19234         return this.getGroupValue();
19235     }
19236     
19237 });
19238
19239  
19240 //<script type="text/javascript">
19241
19242 /*
19243  * Based  Ext JS Library 1.1.1
19244  * Copyright(c) 2006-2007, Ext JS, LLC.
19245  * LGPL
19246  *
19247  */
19248  
19249 /**
19250  * @class Roo.HtmlEditorCore
19251  * @extends Roo.Component
19252  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19253  *
19254  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19255  */
19256
19257 Roo.HtmlEditorCore = function(config){
19258     
19259     
19260     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19261     
19262     
19263     this.addEvents({
19264         /**
19265          * @event initialize
19266          * Fires when the editor is fully initialized (including the iframe)
19267          * @param {Roo.HtmlEditorCore} this
19268          */
19269         initialize: true,
19270         /**
19271          * @event activate
19272          * Fires when the editor is first receives the focus. Any insertion must wait
19273          * until after this event.
19274          * @param {Roo.HtmlEditorCore} this
19275          */
19276         activate: true,
19277          /**
19278          * @event beforesync
19279          * Fires before the textarea is updated with content from the editor iframe. Return false
19280          * to cancel the sync.
19281          * @param {Roo.HtmlEditorCore} this
19282          * @param {String} html
19283          */
19284         beforesync: true,
19285          /**
19286          * @event beforepush
19287          * Fires before the iframe editor is updated with content from the textarea. Return false
19288          * to cancel the push.
19289          * @param {Roo.HtmlEditorCore} this
19290          * @param {String} html
19291          */
19292         beforepush: true,
19293          /**
19294          * @event sync
19295          * Fires when the textarea is updated with content from the editor iframe.
19296          * @param {Roo.HtmlEditorCore} this
19297          * @param {String} html
19298          */
19299         sync: true,
19300          /**
19301          * @event push
19302          * Fires when the iframe editor is updated with content from the textarea.
19303          * @param {Roo.HtmlEditorCore} this
19304          * @param {String} html
19305          */
19306         push: true,
19307         
19308         /**
19309          * @event editorevent
19310          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19311          * @param {Roo.HtmlEditorCore} this
19312          */
19313         editorevent: true
19314         
19315     });
19316     
19317     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19318     
19319     // defaults : white / black...
19320     this.applyBlacklists();
19321     
19322     
19323     
19324 };
19325
19326
19327 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19328
19329
19330      /**
19331      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19332      */
19333     
19334     owner : false,
19335     
19336      /**
19337      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19338      *                        Roo.resizable.
19339      */
19340     resizable : false,
19341      /**
19342      * @cfg {Number} height (in pixels)
19343      */   
19344     height: 300,
19345    /**
19346      * @cfg {Number} width (in pixels)
19347      */   
19348     width: 500,
19349     
19350     /**
19351      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19352      * 
19353      */
19354     stylesheets: false,
19355     
19356     // id of frame..
19357     frameId: false,
19358     
19359     // private properties
19360     validationEvent : false,
19361     deferHeight: true,
19362     initialized : false,
19363     activated : false,
19364     sourceEditMode : false,
19365     onFocus : Roo.emptyFn,
19366     iframePad:3,
19367     hideMode:'offsets',
19368     
19369     clearUp: true,
19370     
19371     // blacklist + whitelisted elements..
19372     black: false,
19373     white: false,
19374      
19375     
19376
19377     /**
19378      * Protected method that will not generally be called directly. It
19379      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19380      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19381      */
19382     getDocMarkup : function(){
19383         // body styles..
19384         var st = '';
19385         
19386         // inherit styels from page...?? 
19387         if (this.stylesheets === false) {
19388             
19389             Roo.get(document.head).select('style').each(function(node) {
19390                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19391             });
19392             
19393             Roo.get(document.head).select('link').each(function(node) { 
19394                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19395             });
19396             
19397         } else if (!this.stylesheets.length) {
19398                 // simple..
19399                 st = '<style type="text/css">' +
19400                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19401                    '</style>';
19402         } else { 
19403             
19404         }
19405         
19406         st +=  '<style type="text/css">' +
19407             'IMG { cursor: pointer } ' +
19408         '</style>';
19409
19410         
19411         return '<html><head>' + st  +
19412             //<style type="text/css">' +
19413             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19414             //'</style>' +
19415             ' </head><body class="roo-htmleditor-body"></body></html>';
19416     },
19417
19418     // private
19419     onRender : function(ct, position)
19420     {
19421         var _t = this;
19422         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19423         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19424         
19425         
19426         this.el.dom.style.border = '0 none';
19427         this.el.dom.setAttribute('tabIndex', -1);
19428         this.el.addClass('x-hidden hide');
19429         
19430         
19431         
19432         if(Roo.isIE){ // fix IE 1px bogus margin
19433             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19434         }
19435        
19436         
19437         this.frameId = Roo.id();
19438         
19439          
19440         
19441         var iframe = this.owner.wrap.createChild({
19442             tag: 'iframe',
19443             cls: 'form-control', // bootstrap..
19444             id: this.frameId,
19445             name: this.frameId,
19446             frameBorder : 'no',
19447             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19448         }, this.el
19449         );
19450         
19451         
19452         this.iframe = iframe.dom;
19453
19454          this.assignDocWin();
19455         
19456         this.doc.designMode = 'on';
19457        
19458         this.doc.open();
19459         this.doc.write(this.getDocMarkup());
19460         this.doc.close();
19461
19462         
19463         var task = { // must defer to wait for browser to be ready
19464             run : function(){
19465                 //console.log("run task?" + this.doc.readyState);
19466                 this.assignDocWin();
19467                 if(this.doc.body || this.doc.readyState == 'complete'){
19468                     try {
19469                         this.doc.designMode="on";
19470                     } catch (e) {
19471                         return;
19472                     }
19473                     Roo.TaskMgr.stop(task);
19474                     this.initEditor.defer(10, this);
19475                 }
19476             },
19477             interval : 10,
19478             duration: 10000,
19479             scope: this
19480         };
19481         Roo.TaskMgr.start(task);
19482
19483     },
19484
19485     // private
19486     onResize : function(w, h)
19487     {
19488          Roo.log('resize: ' +w + ',' + h );
19489         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19490         if(!this.iframe){
19491             return;
19492         }
19493         if(typeof w == 'number'){
19494             
19495             this.iframe.style.width = w + 'px';
19496         }
19497         if(typeof h == 'number'){
19498             
19499             this.iframe.style.height = h + 'px';
19500             if(this.doc){
19501                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19502             }
19503         }
19504         
19505     },
19506
19507     /**
19508      * Toggles the editor between standard and source edit mode.
19509      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19510      */
19511     toggleSourceEdit : function(sourceEditMode){
19512         
19513         this.sourceEditMode = sourceEditMode === true;
19514         
19515         if(this.sourceEditMode){
19516  
19517             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19518             
19519         }else{
19520             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19521             //this.iframe.className = '';
19522             this.deferFocus();
19523         }
19524         //this.setSize(this.owner.wrap.getSize());
19525         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19526     },
19527
19528     
19529   
19530
19531     /**
19532      * Protected method that will not generally be called directly. If you need/want
19533      * custom HTML cleanup, this is the method you should override.
19534      * @param {String} html The HTML to be cleaned
19535      * return {String} The cleaned HTML
19536      */
19537     cleanHtml : function(html){
19538         html = String(html);
19539         if(html.length > 5){
19540             if(Roo.isSafari){ // strip safari nonsense
19541                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19542             }
19543         }
19544         if(html == '&nbsp;'){
19545             html = '';
19546         }
19547         return html;
19548     },
19549
19550     /**
19551      * HTML Editor -> Textarea
19552      * Protected method that will not generally be called directly. Syncs the contents
19553      * of the editor iframe with the textarea.
19554      */
19555     syncValue : function(){
19556         if(this.initialized){
19557             var bd = (this.doc.body || this.doc.documentElement);
19558             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19559             var html = bd.innerHTML;
19560             if(Roo.isSafari){
19561                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19562                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19563                 if(m && m[1]){
19564                     html = '<div style="'+m[0]+'">' + html + '</div>';
19565                 }
19566             }
19567             html = this.cleanHtml(html);
19568             // fix up the special chars.. normaly like back quotes in word...
19569             // however we do not want to do this with chinese..
19570             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19571                 var cc = b.charCodeAt();
19572                 if (
19573                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19574                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19575                     (cc >= 0xf900 && cc < 0xfb00 )
19576                 ) {
19577                         return b;
19578                 }
19579                 return "&#"+cc+";" 
19580             });
19581             if(this.owner.fireEvent('beforesync', this, html) !== false){
19582                 this.el.dom.value = html;
19583                 this.owner.fireEvent('sync', this, html);
19584             }
19585         }
19586     },
19587
19588     /**
19589      * Protected method that will not generally be called directly. Pushes the value of the textarea
19590      * into the iframe editor.
19591      */
19592     pushValue : function(){
19593         if(this.initialized){
19594             var v = this.el.dom.value.trim();
19595             
19596 //            if(v.length < 1){
19597 //                v = '&#160;';
19598 //            }
19599             
19600             if(this.owner.fireEvent('beforepush', this, v) !== false){
19601                 var d = (this.doc.body || this.doc.documentElement);
19602                 d.innerHTML = v;
19603                 this.cleanUpPaste();
19604                 this.el.dom.value = d.innerHTML;
19605                 this.owner.fireEvent('push', this, v);
19606             }
19607         }
19608     },
19609
19610     // private
19611     deferFocus : function(){
19612         this.focus.defer(10, this);
19613     },
19614
19615     // doc'ed in Field
19616     focus : function(){
19617         if(this.win && !this.sourceEditMode){
19618             this.win.focus();
19619         }else{
19620             this.el.focus();
19621         }
19622     },
19623     
19624     assignDocWin: function()
19625     {
19626         var iframe = this.iframe;
19627         
19628          if(Roo.isIE){
19629             this.doc = iframe.contentWindow.document;
19630             this.win = iframe.contentWindow;
19631         } else {
19632 //            if (!Roo.get(this.frameId)) {
19633 //                return;
19634 //            }
19635 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19636 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19637             
19638             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19639                 return;
19640             }
19641             
19642             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19643             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19644         }
19645     },
19646     
19647     // private
19648     initEditor : function(){
19649         //console.log("INIT EDITOR");
19650         this.assignDocWin();
19651         
19652         
19653         
19654         this.doc.designMode="on";
19655         this.doc.open();
19656         this.doc.write(this.getDocMarkup());
19657         this.doc.close();
19658         
19659         var dbody = (this.doc.body || this.doc.documentElement);
19660         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19661         // this copies styles from the containing element into thsi one..
19662         // not sure why we need all of this..
19663         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19664         
19665         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19666         //ss['background-attachment'] = 'fixed'; // w3c
19667         dbody.bgProperties = 'fixed'; // ie
19668         //Roo.DomHelper.applyStyles(dbody, ss);
19669         Roo.EventManager.on(this.doc, {
19670             //'mousedown': this.onEditorEvent,
19671             'mouseup': this.onEditorEvent,
19672             'dblclick': this.onEditorEvent,
19673             'click': this.onEditorEvent,
19674             'keyup': this.onEditorEvent,
19675             buffer:100,
19676             scope: this
19677         });
19678         if(Roo.isGecko){
19679             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19680         }
19681         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19682             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19683         }
19684         this.initialized = true;
19685
19686         this.owner.fireEvent('initialize', this);
19687         this.pushValue();
19688     },
19689
19690     // private
19691     onDestroy : function(){
19692         
19693         
19694         
19695         if(this.rendered){
19696             
19697             //for (var i =0; i < this.toolbars.length;i++) {
19698             //    // fixme - ask toolbars for heights?
19699             //    this.toolbars[i].onDestroy();
19700            // }
19701             
19702             //this.wrap.dom.innerHTML = '';
19703             //this.wrap.remove();
19704         }
19705     },
19706
19707     // private
19708     onFirstFocus : function(){
19709         
19710         this.assignDocWin();
19711         
19712         
19713         this.activated = true;
19714          
19715     
19716         if(Roo.isGecko){ // prevent silly gecko errors
19717             this.win.focus();
19718             var s = this.win.getSelection();
19719             if(!s.focusNode || s.focusNode.nodeType != 3){
19720                 var r = s.getRangeAt(0);
19721                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19722                 r.collapse(true);
19723                 this.deferFocus();
19724             }
19725             try{
19726                 this.execCmd('useCSS', true);
19727                 this.execCmd('styleWithCSS', false);
19728             }catch(e){}
19729         }
19730         this.owner.fireEvent('activate', this);
19731     },
19732
19733     // private
19734     adjustFont: function(btn){
19735         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19736         //if(Roo.isSafari){ // safari
19737         //    adjust *= 2;
19738        // }
19739         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19740         if(Roo.isSafari){ // safari
19741             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19742             v =  (v < 10) ? 10 : v;
19743             v =  (v > 48) ? 48 : v;
19744             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19745             
19746         }
19747         
19748         
19749         v = Math.max(1, v+adjust);
19750         
19751         this.execCmd('FontSize', v  );
19752     },
19753
19754     onEditorEvent : function(e)
19755     {
19756         this.owner.fireEvent('editorevent', this, e);
19757       //  this.updateToolbar();
19758         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19759     },
19760
19761     insertTag : function(tg)
19762     {
19763         // could be a bit smarter... -> wrap the current selected tRoo..
19764         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19765             
19766             range = this.createRange(this.getSelection());
19767             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19768             wrappingNode.appendChild(range.extractContents());
19769             range.insertNode(wrappingNode);
19770
19771             return;
19772             
19773             
19774             
19775         }
19776         this.execCmd("formatblock",   tg);
19777         
19778     },
19779     
19780     insertText : function(txt)
19781     {
19782         
19783         
19784         var range = this.createRange();
19785         range.deleteContents();
19786                //alert(Sender.getAttribute('label'));
19787                
19788         range.insertNode(this.doc.createTextNode(txt));
19789     } ,
19790     
19791      
19792
19793     /**
19794      * Executes a Midas editor command on the editor document and performs necessary focus and
19795      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19796      * @param {String} cmd The Midas command
19797      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19798      */
19799     relayCmd : function(cmd, value){
19800         this.win.focus();
19801         this.execCmd(cmd, value);
19802         this.owner.fireEvent('editorevent', this);
19803         //this.updateToolbar();
19804         this.owner.deferFocus();
19805     },
19806
19807     /**
19808      * Executes a Midas editor command directly on the editor document.
19809      * For visual commands, you should use {@link #relayCmd} instead.
19810      * <b>This should only be called after the editor is initialized.</b>
19811      * @param {String} cmd The Midas command
19812      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19813      */
19814     execCmd : function(cmd, value){
19815         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19816         this.syncValue();
19817     },
19818  
19819  
19820    
19821     /**
19822      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19823      * to insert tRoo.
19824      * @param {String} text | dom node.. 
19825      */
19826     insertAtCursor : function(text)
19827     {
19828         
19829         
19830         
19831         if(!this.activated){
19832             return;
19833         }
19834         /*
19835         if(Roo.isIE){
19836             this.win.focus();
19837             var r = this.doc.selection.createRange();
19838             if(r){
19839                 r.collapse(true);
19840                 r.pasteHTML(text);
19841                 this.syncValue();
19842                 this.deferFocus();
19843             
19844             }
19845             return;
19846         }
19847         */
19848         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19849             this.win.focus();
19850             
19851             
19852             // from jquery ui (MIT licenced)
19853             var range, node;
19854             var win = this.win;
19855             
19856             if (win.getSelection && win.getSelection().getRangeAt) {
19857                 range = win.getSelection().getRangeAt(0);
19858                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19859                 range.insertNode(node);
19860             } else if (win.document.selection && win.document.selection.createRange) {
19861                 // no firefox support
19862                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19863                 win.document.selection.createRange().pasteHTML(txt);
19864             } else {
19865                 // no firefox support
19866                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19867                 this.execCmd('InsertHTML', txt);
19868             } 
19869             
19870             this.syncValue();
19871             
19872             this.deferFocus();
19873         }
19874     },
19875  // private
19876     mozKeyPress : function(e){
19877         if(e.ctrlKey){
19878             var c = e.getCharCode(), cmd;
19879           
19880             if(c > 0){
19881                 c = String.fromCharCode(c).toLowerCase();
19882                 switch(c){
19883                     case 'b':
19884                         cmd = 'bold';
19885                         break;
19886                     case 'i':
19887                         cmd = 'italic';
19888                         break;
19889                     
19890                     case 'u':
19891                         cmd = 'underline';
19892                         break;
19893                     
19894                     case 'v':
19895                         this.cleanUpPaste.defer(100, this);
19896                         return;
19897                         
19898                 }
19899                 if(cmd){
19900                     this.win.focus();
19901                     this.execCmd(cmd);
19902                     this.deferFocus();
19903                     e.preventDefault();
19904                 }
19905                 
19906             }
19907         }
19908     },
19909
19910     // private
19911     fixKeys : function(){ // load time branching for fastest keydown performance
19912         if(Roo.isIE){
19913             return function(e){
19914                 var k = e.getKey(), r;
19915                 if(k == e.TAB){
19916                     e.stopEvent();
19917                     r = this.doc.selection.createRange();
19918                     if(r){
19919                         r.collapse(true);
19920                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19921                         this.deferFocus();
19922                     }
19923                     return;
19924                 }
19925                 
19926                 if(k == e.ENTER){
19927                     r = this.doc.selection.createRange();
19928                     if(r){
19929                         var target = r.parentElement();
19930                         if(!target || target.tagName.toLowerCase() != 'li'){
19931                             e.stopEvent();
19932                             r.pasteHTML('<br />');
19933                             r.collapse(false);
19934                             r.select();
19935                         }
19936                     }
19937                 }
19938                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19939                     this.cleanUpPaste.defer(100, this);
19940                     return;
19941                 }
19942                 
19943                 
19944             };
19945         }else if(Roo.isOpera){
19946             return function(e){
19947                 var k = e.getKey();
19948                 if(k == e.TAB){
19949                     e.stopEvent();
19950                     this.win.focus();
19951                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19952                     this.deferFocus();
19953                 }
19954                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19955                     this.cleanUpPaste.defer(100, this);
19956                     return;
19957                 }
19958                 
19959             };
19960         }else if(Roo.isSafari){
19961             return function(e){
19962                 var k = e.getKey();
19963                 
19964                 if(k == e.TAB){
19965                     e.stopEvent();
19966                     this.execCmd('InsertText','\t');
19967                     this.deferFocus();
19968                     return;
19969                 }
19970                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19971                     this.cleanUpPaste.defer(100, this);
19972                     return;
19973                 }
19974                 
19975              };
19976         }
19977     }(),
19978     
19979     getAllAncestors: function()
19980     {
19981         var p = this.getSelectedNode();
19982         var a = [];
19983         if (!p) {
19984             a.push(p); // push blank onto stack..
19985             p = this.getParentElement();
19986         }
19987         
19988         
19989         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19990             a.push(p);
19991             p = p.parentNode;
19992         }
19993         a.push(this.doc.body);
19994         return a;
19995     },
19996     lastSel : false,
19997     lastSelNode : false,
19998     
19999     
20000     getSelection : function() 
20001     {
20002         this.assignDocWin();
20003         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20004     },
20005     
20006     getSelectedNode: function() 
20007     {
20008         // this may only work on Gecko!!!
20009         
20010         // should we cache this!!!!
20011         
20012         
20013         
20014          
20015         var range = this.createRange(this.getSelection()).cloneRange();
20016         
20017         if (Roo.isIE) {
20018             var parent = range.parentElement();
20019             while (true) {
20020                 var testRange = range.duplicate();
20021                 testRange.moveToElementText(parent);
20022                 if (testRange.inRange(range)) {
20023                     break;
20024                 }
20025                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20026                     break;
20027                 }
20028                 parent = parent.parentElement;
20029             }
20030             return parent;
20031         }
20032         
20033         // is ancestor a text element.
20034         var ac =  range.commonAncestorContainer;
20035         if (ac.nodeType == 3) {
20036             ac = ac.parentNode;
20037         }
20038         
20039         var ar = ac.childNodes;
20040          
20041         var nodes = [];
20042         var other_nodes = [];
20043         var has_other_nodes = false;
20044         for (var i=0;i<ar.length;i++) {
20045             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20046                 continue;
20047             }
20048             // fullly contained node.
20049             
20050             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20051                 nodes.push(ar[i]);
20052                 continue;
20053             }
20054             
20055             // probably selected..
20056             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20057                 other_nodes.push(ar[i]);
20058                 continue;
20059             }
20060             // outer..
20061             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20062                 continue;
20063             }
20064             
20065             
20066             has_other_nodes = true;
20067         }
20068         if (!nodes.length && other_nodes.length) {
20069             nodes= other_nodes;
20070         }
20071         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20072             return false;
20073         }
20074         
20075         return nodes[0];
20076     },
20077     createRange: function(sel)
20078     {
20079         // this has strange effects when using with 
20080         // top toolbar - not sure if it's a great idea.
20081         //this.editor.contentWindow.focus();
20082         if (typeof sel != "undefined") {
20083             try {
20084                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20085             } catch(e) {
20086                 return this.doc.createRange();
20087             }
20088         } else {
20089             return this.doc.createRange();
20090         }
20091     },
20092     getParentElement: function()
20093     {
20094         
20095         this.assignDocWin();
20096         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20097         
20098         var range = this.createRange(sel);
20099          
20100         try {
20101             var p = range.commonAncestorContainer;
20102             while (p.nodeType == 3) { // text node
20103                 p = p.parentNode;
20104             }
20105             return p;
20106         } catch (e) {
20107             return null;
20108         }
20109     
20110     },
20111     /***
20112      *
20113      * Range intersection.. the hard stuff...
20114      *  '-1' = before
20115      *  '0' = hits..
20116      *  '1' = after.
20117      *         [ -- selected range --- ]
20118      *   [fail]                        [fail]
20119      *
20120      *    basically..
20121      *      if end is before start or  hits it. fail.
20122      *      if start is after end or hits it fail.
20123      *
20124      *   if either hits (but other is outside. - then it's not 
20125      *   
20126      *    
20127      **/
20128     
20129     
20130     // @see http://www.thismuchiknow.co.uk/?p=64.
20131     rangeIntersectsNode : function(range, node)
20132     {
20133         var nodeRange = node.ownerDocument.createRange();
20134         try {
20135             nodeRange.selectNode(node);
20136         } catch (e) {
20137             nodeRange.selectNodeContents(node);
20138         }
20139     
20140         var rangeStartRange = range.cloneRange();
20141         rangeStartRange.collapse(true);
20142     
20143         var rangeEndRange = range.cloneRange();
20144         rangeEndRange.collapse(false);
20145     
20146         var nodeStartRange = nodeRange.cloneRange();
20147         nodeStartRange.collapse(true);
20148     
20149         var nodeEndRange = nodeRange.cloneRange();
20150         nodeEndRange.collapse(false);
20151     
20152         return rangeStartRange.compareBoundaryPoints(
20153                  Range.START_TO_START, nodeEndRange) == -1 &&
20154                rangeEndRange.compareBoundaryPoints(
20155                  Range.START_TO_START, nodeStartRange) == 1;
20156         
20157          
20158     },
20159     rangeCompareNode : function(range, node)
20160     {
20161         var nodeRange = node.ownerDocument.createRange();
20162         try {
20163             nodeRange.selectNode(node);
20164         } catch (e) {
20165             nodeRange.selectNodeContents(node);
20166         }
20167         
20168         
20169         range.collapse(true);
20170     
20171         nodeRange.collapse(true);
20172      
20173         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20174         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20175          
20176         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20177         
20178         var nodeIsBefore   =  ss == 1;
20179         var nodeIsAfter    = ee == -1;
20180         
20181         if (nodeIsBefore && nodeIsAfter) {
20182             return 0; // outer
20183         }
20184         if (!nodeIsBefore && nodeIsAfter) {
20185             return 1; //right trailed.
20186         }
20187         
20188         if (nodeIsBefore && !nodeIsAfter) {
20189             return 2;  // left trailed.
20190         }
20191         // fully contined.
20192         return 3;
20193     },
20194
20195     // private? - in a new class?
20196     cleanUpPaste :  function()
20197     {
20198         // cleans up the whole document..
20199         Roo.log('cleanuppaste');
20200         
20201         this.cleanUpChildren(this.doc.body);
20202         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20203         if (clean != this.doc.body.innerHTML) {
20204             this.doc.body.innerHTML = clean;
20205         }
20206         
20207     },
20208     
20209     cleanWordChars : function(input) {// change the chars to hex code
20210         var he = Roo.HtmlEditorCore;
20211         
20212         var output = input;
20213         Roo.each(he.swapCodes, function(sw) { 
20214             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20215             
20216             output = output.replace(swapper, sw[1]);
20217         });
20218         
20219         return output;
20220     },
20221     
20222     
20223     cleanUpChildren : function (n)
20224     {
20225         if (!n.childNodes.length) {
20226             return;
20227         }
20228         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20229            this.cleanUpChild(n.childNodes[i]);
20230         }
20231     },
20232     
20233     
20234         
20235     
20236     cleanUpChild : function (node)
20237     {
20238         var ed = this;
20239         //console.log(node);
20240         if (node.nodeName == "#text") {
20241             // clean up silly Windows -- stuff?
20242             return; 
20243         }
20244         if (node.nodeName == "#comment") {
20245             node.parentNode.removeChild(node);
20246             // clean up silly Windows -- stuff?
20247             return; 
20248         }
20249         var lcname = node.tagName.toLowerCase();
20250         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20251         // whitelist of tags..
20252         
20253         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20254             // remove node.
20255             node.parentNode.removeChild(node);
20256             return;
20257             
20258         }
20259         
20260         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20261         
20262         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20263         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20264         
20265         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20266         //    remove_keep_children = true;
20267         //}
20268         
20269         if (remove_keep_children) {
20270             this.cleanUpChildren(node);
20271             // inserts everything just before this node...
20272             while (node.childNodes.length) {
20273                 var cn = node.childNodes[0];
20274                 node.removeChild(cn);
20275                 node.parentNode.insertBefore(cn, node);
20276             }
20277             node.parentNode.removeChild(node);
20278             return;
20279         }
20280         
20281         if (!node.attributes || !node.attributes.length) {
20282             this.cleanUpChildren(node);
20283             return;
20284         }
20285         
20286         function cleanAttr(n,v)
20287         {
20288             
20289             if (v.match(/^\./) || v.match(/^\//)) {
20290                 return;
20291             }
20292             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20293                 return;
20294             }
20295             if (v.match(/^#/)) {
20296                 return;
20297             }
20298 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20299             node.removeAttribute(n);
20300             
20301         }
20302         
20303         var cwhite = this.cwhite;
20304         var cblack = this.cblack;
20305             
20306         function cleanStyle(n,v)
20307         {
20308             if (v.match(/expression/)) { //XSS?? should we even bother..
20309                 node.removeAttribute(n);
20310                 return;
20311             }
20312             
20313             var parts = v.split(/;/);
20314             var clean = [];
20315             
20316             Roo.each(parts, function(p) {
20317                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20318                 if (!p.length) {
20319                     return true;
20320                 }
20321                 var l = p.split(':').shift().replace(/\s+/g,'');
20322                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20323                 
20324                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20325 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20326                     //node.removeAttribute(n);
20327                     return true;
20328                 }
20329                 //Roo.log()
20330                 // only allow 'c whitelisted system attributes'
20331                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20332 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20333                     //node.removeAttribute(n);
20334                     return true;
20335                 }
20336                 
20337                 
20338                  
20339                 
20340                 clean.push(p);
20341                 return true;
20342             });
20343             if (clean.length) { 
20344                 node.setAttribute(n, clean.join(';'));
20345             } else {
20346                 node.removeAttribute(n);
20347             }
20348             
20349         }
20350         
20351         
20352         for (var i = node.attributes.length-1; i > -1 ; i--) {
20353             var a = node.attributes[i];
20354             //console.log(a);
20355             
20356             if (a.name.toLowerCase().substr(0,2)=='on')  {
20357                 node.removeAttribute(a.name);
20358                 continue;
20359             }
20360             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20361                 node.removeAttribute(a.name);
20362                 continue;
20363             }
20364             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20365                 cleanAttr(a.name,a.value); // fixme..
20366                 continue;
20367             }
20368             if (a.name == 'style') {
20369                 cleanStyle(a.name,a.value);
20370                 continue;
20371             }
20372             /// clean up MS crap..
20373             // tecnically this should be a list of valid class'es..
20374             
20375             
20376             if (a.name == 'class') {
20377                 if (a.value.match(/^Mso/)) {
20378                     node.className = '';
20379                 }
20380                 
20381                 if (a.value.match(/body/)) {
20382                     node.className = '';
20383                 }
20384                 continue;
20385             }
20386             
20387             // style cleanup!?
20388             // class cleanup?
20389             
20390         }
20391         
20392         
20393         this.cleanUpChildren(node);
20394         
20395         
20396     },
20397     
20398     /**
20399      * Clean up MS wordisms...
20400      */
20401     cleanWord : function(node)
20402     {
20403         
20404         
20405         if (!node) {
20406             this.cleanWord(this.doc.body);
20407             return;
20408         }
20409         if (node.nodeName == "#text") {
20410             // clean up silly Windows -- stuff?
20411             return; 
20412         }
20413         if (node.nodeName == "#comment") {
20414             node.parentNode.removeChild(node);
20415             // clean up silly Windows -- stuff?
20416             return; 
20417         }
20418         
20419         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20420             node.parentNode.removeChild(node);
20421             return;
20422         }
20423         
20424         // remove - but keep children..
20425         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20426             while (node.childNodes.length) {
20427                 var cn = node.childNodes[0];
20428                 node.removeChild(cn);
20429                 node.parentNode.insertBefore(cn, node);
20430             }
20431             node.parentNode.removeChild(node);
20432             this.iterateChildren(node, this.cleanWord);
20433             return;
20434         }
20435         // clean styles
20436         if (node.className.length) {
20437             
20438             var cn = node.className.split(/\W+/);
20439             var cna = [];
20440             Roo.each(cn, function(cls) {
20441                 if (cls.match(/Mso[a-zA-Z]+/)) {
20442                     return;
20443                 }
20444                 cna.push(cls);
20445             });
20446             node.className = cna.length ? cna.join(' ') : '';
20447             if (!cna.length) {
20448                 node.removeAttribute("class");
20449             }
20450         }
20451         
20452         if (node.hasAttribute("lang")) {
20453             node.removeAttribute("lang");
20454         }
20455         
20456         if (node.hasAttribute("style")) {
20457             
20458             var styles = node.getAttribute("style").split(";");
20459             var nstyle = [];
20460             Roo.each(styles, function(s) {
20461                 if (!s.match(/:/)) {
20462                     return;
20463                 }
20464                 var kv = s.split(":");
20465                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20466                     return;
20467                 }
20468                 // what ever is left... we allow.
20469                 nstyle.push(s);
20470             });
20471             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20472             if (!nstyle.length) {
20473                 node.removeAttribute('style');
20474             }
20475         }
20476         this.iterateChildren(node, this.cleanWord);
20477         
20478         
20479         
20480     },
20481     /**
20482      * iterateChildren of a Node, calling fn each time, using this as the scole..
20483      * @param {DomNode} node node to iterate children of.
20484      * @param {Function} fn method of this class to call on each item.
20485      */
20486     iterateChildren : function(node, fn)
20487     {
20488         if (!node.childNodes.length) {
20489                 return;
20490         }
20491         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20492            fn.call(this, node.childNodes[i])
20493         }
20494     },
20495     
20496     
20497     /**
20498      * cleanTableWidths.
20499      *
20500      * Quite often pasting from word etc.. results in tables with column and widths.
20501      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20502      *
20503      */
20504     cleanTableWidths : function(node)
20505     {
20506          
20507          
20508         if (!node) {
20509             this.cleanTableWidths(this.doc.body);
20510             return;
20511         }
20512         
20513         // ignore list...
20514         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20515             return; 
20516         }
20517         Roo.log(node.tagName);
20518         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20519             this.iterateChildren(node, this.cleanTableWidths);
20520             return;
20521         }
20522         if (node.hasAttribute('width')) {
20523             node.removeAttribute('width');
20524         }
20525         
20526          
20527         if (node.hasAttribute("style")) {
20528             // pretty basic...
20529             
20530             var styles = node.getAttribute("style").split(";");
20531             var nstyle = [];
20532             Roo.each(styles, function(s) {
20533                 if (!s.match(/:/)) {
20534                     return;
20535                 }
20536                 var kv = s.split(":");
20537                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20538                     return;
20539                 }
20540                 // what ever is left... we allow.
20541                 nstyle.push(s);
20542             });
20543             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20544             if (!nstyle.length) {
20545                 node.removeAttribute('style');
20546             }
20547         }
20548         
20549         this.iterateChildren(node, this.cleanTableWidths);
20550         
20551         
20552     },
20553     
20554     
20555     
20556     
20557     domToHTML : function(currentElement, depth, nopadtext) {
20558         
20559         depth = depth || 0;
20560         nopadtext = nopadtext || false;
20561     
20562         if (!currentElement) {
20563             return this.domToHTML(this.doc.body);
20564         }
20565         
20566         //Roo.log(currentElement);
20567         var j;
20568         var allText = false;
20569         var nodeName = currentElement.nodeName;
20570         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20571         
20572         if  (nodeName == '#text') {
20573             
20574             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20575         }
20576         
20577         
20578         var ret = '';
20579         if (nodeName != 'BODY') {
20580              
20581             var i = 0;
20582             // Prints the node tagName, such as <A>, <IMG>, etc
20583             if (tagName) {
20584                 var attr = [];
20585                 for(i = 0; i < currentElement.attributes.length;i++) {
20586                     // quoting?
20587                     var aname = currentElement.attributes.item(i).name;
20588                     if (!currentElement.attributes.item(i).value.length) {
20589                         continue;
20590                     }
20591                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20592                 }
20593                 
20594                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20595             } 
20596             else {
20597                 
20598                 // eack
20599             }
20600         } else {
20601             tagName = false;
20602         }
20603         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20604             return ret;
20605         }
20606         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20607             nopadtext = true;
20608         }
20609         
20610         
20611         // Traverse the tree
20612         i = 0;
20613         var currentElementChild = currentElement.childNodes.item(i);
20614         var allText = true;
20615         var innerHTML  = '';
20616         lastnode = '';
20617         while (currentElementChild) {
20618             // Formatting code (indent the tree so it looks nice on the screen)
20619             var nopad = nopadtext;
20620             if (lastnode == 'SPAN') {
20621                 nopad  = true;
20622             }
20623             // text
20624             if  (currentElementChild.nodeName == '#text') {
20625                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20626                 toadd = nopadtext ? toadd : toadd.trim();
20627                 if (!nopad && toadd.length > 80) {
20628                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20629                 }
20630                 innerHTML  += toadd;
20631                 
20632                 i++;
20633                 currentElementChild = currentElement.childNodes.item(i);
20634                 lastNode = '';
20635                 continue;
20636             }
20637             allText = false;
20638             
20639             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20640                 
20641             // Recursively traverse the tree structure of the child node
20642             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20643             lastnode = currentElementChild.nodeName;
20644             i++;
20645             currentElementChild=currentElement.childNodes.item(i);
20646         }
20647         
20648         ret += innerHTML;
20649         
20650         if (!allText) {
20651                 // The remaining code is mostly for formatting the tree
20652             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20653         }
20654         
20655         
20656         if (tagName) {
20657             ret+= "</"+tagName+">";
20658         }
20659         return ret;
20660         
20661     },
20662         
20663     applyBlacklists : function()
20664     {
20665         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20666         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20667         
20668         this.white = [];
20669         this.black = [];
20670         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20671             if (b.indexOf(tag) > -1) {
20672                 return;
20673             }
20674             this.white.push(tag);
20675             
20676         }, this);
20677         
20678         Roo.each(w, function(tag) {
20679             if (b.indexOf(tag) > -1) {
20680                 return;
20681             }
20682             if (this.white.indexOf(tag) > -1) {
20683                 return;
20684             }
20685             this.white.push(tag);
20686             
20687         }, this);
20688         
20689         
20690         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20691             if (w.indexOf(tag) > -1) {
20692                 return;
20693             }
20694             this.black.push(tag);
20695             
20696         }, this);
20697         
20698         Roo.each(b, function(tag) {
20699             if (w.indexOf(tag) > -1) {
20700                 return;
20701             }
20702             if (this.black.indexOf(tag) > -1) {
20703                 return;
20704             }
20705             this.black.push(tag);
20706             
20707         }, this);
20708         
20709         
20710         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20711         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20712         
20713         this.cwhite = [];
20714         this.cblack = [];
20715         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20716             if (b.indexOf(tag) > -1) {
20717                 return;
20718             }
20719             this.cwhite.push(tag);
20720             
20721         }, this);
20722         
20723         Roo.each(w, function(tag) {
20724             if (b.indexOf(tag) > -1) {
20725                 return;
20726             }
20727             if (this.cwhite.indexOf(tag) > -1) {
20728                 return;
20729             }
20730             this.cwhite.push(tag);
20731             
20732         }, this);
20733         
20734         
20735         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20736             if (w.indexOf(tag) > -1) {
20737                 return;
20738             }
20739             this.cblack.push(tag);
20740             
20741         }, this);
20742         
20743         Roo.each(b, function(tag) {
20744             if (w.indexOf(tag) > -1) {
20745                 return;
20746             }
20747             if (this.cblack.indexOf(tag) > -1) {
20748                 return;
20749             }
20750             this.cblack.push(tag);
20751             
20752         }, this);
20753     },
20754     
20755     setStylesheets : function(stylesheets)
20756     {
20757         if(typeof(stylesheets) == 'string'){
20758             Roo.get(this.iframe.contentDocument.head).createChild({
20759                 tag : 'link',
20760                 rel : 'stylesheet',
20761                 type : 'text/css',
20762                 href : stylesheets
20763             });
20764             
20765             return;
20766         }
20767         var _this = this;
20768      
20769         Roo.each(stylesheets, function(s) {
20770             if(!s.length){
20771                 return;
20772             }
20773             
20774             Roo.get(_this.iframe.contentDocument.head).createChild({
20775                 tag : 'link',
20776                 rel : 'stylesheet',
20777                 type : 'text/css',
20778                 href : s
20779             });
20780         });
20781
20782         
20783     },
20784     
20785     removeStylesheets : function()
20786     {
20787         var _this = this;
20788         
20789         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20790             s.remove();
20791         });
20792     }
20793     
20794     // hide stuff that is not compatible
20795     /**
20796      * @event blur
20797      * @hide
20798      */
20799     /**
20800      * @event change
20801      * @hide
20802      */
20803     /**
20804      * @event focus
20805      * @hide
20806      */
20807     /**
20808      * @event specialkey
20809      * @hide
20810      */
20811     /**
20812      * @cfg {String} fieldClass @hide
20813      */
20814     /**
20815      * @cfg {String} focusClass @hide
20816      */
20817     /**
20818      * @cfg {String} autoCreate @hide
20819      */
20820     /**
20821      * @cfg {String} inputType @hide
20822      */
20823     /**
20824      * @cfg {String} invalidClass @hide
20825      */
20826     /**
20827      * @cfg {String} invalidText @hide
20828      */
20829     /**
20830      * @cfg {String} msgFx @hide
20831      */
20832     /**
20833      * @cfg {String} validateOnBlur @hide
20834      */
20835 });
20836
20837 Roo.HtmlEditorCore.white = [
20838         'area', 'br', 'img', 'input', 'hr', 'wbr',
20839         
20840        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20841        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20842        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20843        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20844        'table',   'ul',         'xmp', 
20845        
20846        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20847       'thead',   'tr', 
20848      
20849       'dir', 'menu', 'ol', 'ul', 'dl',
20850        
20851       'embed',  'object'
20852 ];
20853
20854
20855 Roo.HtmlEditorCore.black = [
20856     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20857         'applet', // 
20858         'base',   'basefont', 'bgsound', 'blink',  'body', 
20859         'frame',  'frameset', 'head',    'html',   'ilayer', 
20860         'iframe', 'layer',  'link',     'meta',    'object',   
20861         'script', 'style' ,'title',  'xml' // clean later..
20862 ];
20863 Roo.HtmlEditorCore.clean = [
20864     'script', 'style', 'title', 'xml'
20865 ];
20866 Roo.HtmlEditorCore.remove = [
20867     'font'
20868 ];
20869 // attributes..
20870
20871 Roo.HtmlEditorCore.ablack = [
20872     'on'
20873 ];
20874     
20875 Roo.HtmlEditorCore.aclean = [ 
20876     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20877 ];
20878
20879 // protocols..
20880 Roo.HtmlEditorCore.pwhite= [
20881         'http',  'https',  'mailto'
20882 ];
20883
20884 // white listed style attributes.
20885 Roo.HtmlEditorCore.cwhite= [
20886       //  'text-align', /// default is to allow most things..
20887       
20888          
20889 //        'font-size'//??
20890 ];
20891
20892 // black listed style attributes.
20893 Roo.HtmlEditorCore.cblack= [
20894       //  'font-size' -- this can be set by the project 
20895 ];
20896
20897
20898 Roo.HtmlEditorCore.swapCodes   =[ 
20899     [    8211, "--" ], 
20900     [    8212, "--" ], 
20901     [    8216,  "'" ],  
20902     [    8217, "'" ],  
20903     [    8220, '"' ],  
20904     [    8221, '"' ],  
20905     [    8226, "*" ],  
20906     [    8230, "..." ]
20907 ]; 
20908
20909     /*
20910  * - LGPL
20911  *
20912  * HtmlEditor
20913  * 
20914  */
20915
20916 /**
20917  * @class Roo.bootstrap.HtmlEditor
20918  * @extends Roo.bootstrap.TextArea
20919  * Bootstrap HtmlEditor class
20920
20921  * @constructor
20922  * Create a new HtmlEditor
20923  * @param {Object} config The config object
20924  */
20925
20926 Roo.bootstrap.HtmlEditor = function(config){
20927     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20928     if (!this.toolbars) {
20929         this.toolbars = [];
20930     }
20931     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20932     this.addEvents({
20933             /**
20934              * @event initialize
20935              * Fires when the editor is fully initialized (including the iframe)
20936              * @param {HtmlEditor} this
20937              */
20938             initialize: true,
20939             /**
20940              * @event activate
20941              * Fires when the editor is first receives the focus. Any insertion must wait
20942              * until after this event.
20943              * @param {HtmlEditor} this
20944              */
20945             activate: true,
20946              /**
20947              * @event beforesync
20948              * Fires before the textarea is updated with content from the editor iframe. Return false
20949              * to cancel the sync.
20950              * @param {HtmlEditor} this
20951              * @param {String} html
20952              */
20953             beforesync: true,
20954              /**
20955              * @event beforepush
20956              * Fires before the iframe editor is updated with content from the textarea. Return false
20957              * to cancel the push.
20958              * @param {HtmlEditor} this
20959              * @param {String} html
20960              */
20961             beforepush: true,
20962              /**
20963              * @event sync
20964              * Fires when the textarea is updated with content from the editor iframe.
20965              * @param {HtmlEditor} this
20966              * @param {String} html
20967              */
20968             sync: true,
20969              /**
20970              * @event push
20971              * Fires when the iframe editor is updated with content from the textarea.
20972              * @param {HtmlEditor} this
20973              * @param {String} html
20974              */
20975             push: true,
20976              /**
20977              * @event editmodechange
20978              * Fires when the editor switches edit modes
20979              * @param {HtmlEditor} this
20980              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20981              */
20982             editmodechange: true,
20983             /**
20984              * @event editorevent
20985              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20986              * @param {HtmlEditor} this
20987              */
20988             editorevent: true,
20989             /**
20990              * @event firstfocus
20991              * Fires when on first focus - needed by toolbars..
20992              * @param {HtmlEditor} this
20993              */
20994             firstfocus: true,
20995             /**
20996              * @event autosave
20997              * Auto save the htmlEditor value as a file into Events
20998              * @param {HtmlEditor} this
20999              */
21000             autosave: true,
21001             /**
21002              * @event savedpreview
21003              * preview the saved version of htmlEditor
21004              * @param {HtmlEditor} this
21005              */
21006             savedpreview: true
21007         });
21008 };
21009
21010
21011 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21012     
21013     
21014       /**
21015      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21016      */
21017     toolbars : false,
21018    
21019      /**
21020      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21021      *                        Roo.resizable.
21022      */
21023     resizable : false,
21024      /**
21025      * @cfg {Number} height (in pixels)
21026      */   
21027     height: 300,
21028    /**
21029      * @cfg {Number} width (in pixels)
21030      */   
21031     width: false,
21032     
21033     /**
21034      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21035      * 
21036      */
21037     stylesheets: false,
21038     
21039     // id of frame..
21040     frameId: false,
21041     
21042     // private properties
21043     validationEvent : false,
21044     deferHeight: true,
21045     initialized : false,
21046     activated : false,
21047     
21048     onFocus : Roo.emptyFn,
21049     iframePad:3,
21050     hideMode:'offsets',
21051     
21052     
21053     tbContainer : false,
21054     
21055     toolbarContainer :function() {
21056         return this.wrap.select('.x-html-editor-tb',true).first();
21057     },
21058
21059     /**
21060      * Protected method that will not generally be called directly. It
21061      * is called when the editor creates its toolbar. Override this method if you need to
21062      * add custom toolbar buttons.
21063      * @param {HtmlEditor} editor
21064      */
21065     createToolbar : function(){
21066         
21067         Roo.log("create toolbars");
21068         
21069         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21070         this.toolbars[0].render(this.toolbarContainer());
21071         
21072         return;
21073         
21074 //        if (!editor.toolbars || !editor.toolbars.length) {
21075 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21076 //        }
21077 //        
21078 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21079 //            editor.toolbars[i] = Roo.factory(
21080 //                    typeof(editor.toolbars[i]) == 'string' ?
21081 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21082 //                Roo.bootstrap.HtmlEditor);
21083 //            editor.toolbars[i].init(editor);
21084 //        }
21085     },
21086
21087      
21088     // private
21089     onRender : function(ct, position)
21090     {
21091        // Roo.log("Call onRender: " + this.xtype);
21092         var _t = this;
21093         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21094       
21095         this.wrap = this.inputEl().wrap({
21096             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21097         });
21098         
21099         this.editorcore.onRender(ct, position);
21100          
21101         if (this.resizable) {
21102             this.resizeEl = new Roo.Resizable(this.wrap, {
21103                 pinned : true,
21104                 wrap: true,
21105                 dynamic : true,
21106                 minHeight : this.height,
21107                 height: this.height,
21108                 handles : this.resizable,
21109                 width: this.width,
21110                 listeners : {
21111                     resize : function(r, w, h) {
21112                         _t.onResize(w,h); // -something
21113                     }
21114                 }
21115             });
21116             
21117         }
21118         this.createToolbar(this);
21119        
21120         
21121         if(!this.width && this.resizable){
21122             this.setSize(this.wrap.getSize());
21123         }
21124         if (this.resizeEl) {
21125             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21126             // should trigger onReize..
21127         }
21128         
21129     },
21130
21131     // private
21132     onResize : function(w, h)
21133     {
21134         Roo.log('resize: ' +w + ',' + h );
21135         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21136         var ew = false;
21137         var eh = false;
21138         
21139         if(this.inputEl() ){
21140             if(typeof w == 'number'){
21141                 var aw = w - this.wrap.getFrameWidth('lr');
21142                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21143                 ew = aw;
21144             }
21145             if(typeof h == 'number'){
21146                  var tbh = -11;  // fixme it needs to tool bar size!
21147                 for (var i =0; i < this.toolbars.length;i++) {
21148                     // fixme - ask toolbars for heights?
21149                     tbh += this.toolbars[i].el.getHeight();
21150                     //if (this.toolbars[i].footer) {
21151                     //    tbh += this.toolbars[i].footer.el.getHeight();
21152                     //}
21153                 }
21154               
21155                 
21156                 
21157                 
21158                 
21159                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21160                 ah -= 5; // knock a few pixes off for look..
21161                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21162                 var eh = ah;
21163             }
21164         }
21165         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21166         this.editorcore.onResize(ew,eh);
21167         
21168     },
21169
21170     /**
21171      * Toggles the editor between standard and source edit mode.
21172      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21173      */
21174     toggleSourceEdit : function(sourceEditMode)
21175     {
21176         this.editorcore.toggleSourceEdit(sourceEditMode);
21177         
21178         if(this.editorcore.sourceEditMode){
21179             Roo.log('editor - showing textarea');
21180             
21181 //            Roo.log('in');
21182 //            Roo.log(this.syncValue());
21183             this.syncValue();
21184             this.inputEl().removeClass(['hide', 'x-hidden']);
21185             this.inputEl().dom.removeAttribute('tabIndex');
21186             this.inputEl().focus();
21187         }else{
21188             Roo.log('editor - hiding textarea');
21189 //            Roo.log('out')
21190 //            Roo.log(this.pushValue()); 
21191             this.pushValue();
21192             
21193             this.inputEl().addClass(['hide', 'x-hidden']);
21194             this.inputEl().dom.setAttribute('tabIndex', -1);
21195             //this.deferFocus();
21196         }
21197          
21198         if(this.resizable){
21199             this.setSize(this.wrap.getSize());
21200         }
21201         
21202         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21203     },
21204  
21205     // private (for BoxComponent)
21206     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21207
21208     // private (for BoxComponent)
21209     getResizeEl : function(){
21210         return this.wrap;
21211     },
21212
21213     // private (for BoxComponent)
21214     getPositionEl : function(){
21215         return this.wrap;
21216     },
21217
21218     // private
21219     initEvents : function(){
21220         this.originalValue = this.getValue();
21221     },
21222
21223 //    /**
21224 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21225 //     * @method
21226 //     */
21227 //    markInvalid : Roo.emptyFn,
21228 //    /**
21229 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21230 //     * @method
21231 //     */
21232 //    clearInvalid : Roo.emptyFn,
21233
21234     setValue : function(v){
21235         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21236         this.editorcore.pushValue();
21237     },
21238
21239      
21240     // private
21241     deferFocus : function(){
21242         this.focus.defer(10, this);
21243     },
21244
21245     // doc'ed in Field
21246     focus : function(){
21247         this.editorcore.focus();
21248         
21249     },
21250       
21251
21252     // private
21253     onDestroy : function(){
21254         
21255         
21256         
21257         if(this.rendered){
21258             
21259             for (var i =0; i < this.toolbars.length;i++) {
21260                 // fixme - ask toolbars for heights?
21261                 this.toolbars[i].onDestroy();
21262             }
21263             
21264             this.wrap.dom.innerHTML = '';
21265             this.wrap.remove();
21266         }
21267     },
21268
21269     // private
21270     onFirstFocus : function(){
21271         //Roo.log("onFirstFocus");
21272         this.editorcore.onFirstFocus();
21273          for (var i =0; i < this.toolbars.length;i++) {
21274             this.toolbars[i].onFirstFocus();
21275         }
21276         
21277     },
21278     
21279     // private
21280     syncValue : function()
21281     {   
21282         this.editorcore.syncValue();
21283     },
21284     
21285     pushValue : function()
21286     {   
21287         this.editorcore.pushValue();
21288     }
21289      
21290     
21291     // hide stuff that is not compatible
21292     /**
21293      * @event blur
21294      * @hide
21295      */
21296     /**
21297      * @event change
21298      * @hide
21299      */
21300     /**
21301      * @event focus
21302      * @hide
21303      */
21304     /**
21305      * @event specialkey
21306      * @hide
21307      */
21308     /**
21309      * @cfg {String} fieldClass @hide
21310      */
21311     /**
21312      * @cfg {String} focusClass @hide
21313      */
21314     /**
21315      * @cfg {String} autoCreate @hide
21316      */
21317     /**
21318      * @cfg {String} inputType @hide
21319      */
21320     /**
21321      * @cfg {String} invalidClass @hide
21322      */
21323     /**
21324      * @cfg {String} invalidText @hide
21325      */
21326     /**
21327      * @cfg {String} msgFx @hide
21328      */
21329     /**
21330      * @cfg {String} validateOnBlur @hide
21331      */
21332 });
21333  
21334     
21335    
21336    
21337    
21338       
21339 Roo.namespace('Roo.bootstrap.htmleditor');
21340 /**
21341  * @class Roo.bootstrap.HtmlEditorToolbar1
21342  * Basic Toolbar
21343  * 
21344  * Usage:
21345  *
21346  new Roo.bootstrap.HtmlEditor({
21347     ....
21348     toolbars : [
21349         new Roo.bootstrap.HtmlEditorToolbar1({
21350             disable : { fonts: 1 , format: 1, ..., ... , ...],
21351             btns : [ .... ]
21352         })
21353     }
21354      
21355  * 
21356  * @cfg {Object} disable List of elements to disable..
21357  * @cfg {Array} btns List of additional buttons.
21358  * 
21359  * 
21360  * NEEDS Extra CSS? 
21361  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21362  */
21363  
21364 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21365 {
21366     
21367     Roo.apply(this, config);
21368     
21369     // default disabled, based on 'good practice'..
21370     this.disable = this.disable || {};
21371     Roo.applyIf(this.disable, {
21372         fontSize : true,
21373         colors : true,
21374         specialElements : true
21375     });
21376     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21377     
21378     this.editor = config.editor;
21379     this.editorcore = config.editor.editorcore;
21380     
21381     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21382     
21383     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21384     // dont call parent... till later.
21385 }
21386 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21387      
21388     bar : true,
21389     
21390     editor : false,
21391     editorcore : false,
21392     
21393     
21394     formats : [
21395         "p" ,  
21396         "h1","h2","h3","h4","h5","h6", 
21397         "pre", "code", 
21398         "abbr", "acronym", "address", "cite", "samp", "var",
21399         'div','span'
21400     ],
21401     
21402     onRender : function(ct, position)
21403     {
21404        // Roo.log("Call onRender: " + this.xtype);
21405         
21406        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21407        Roo.log(this.el);
21408        this.el.dom.style.marginBottom = '0';
21409        var _this = this;
21410        var editorcore = this.editorcore;
21411        var editor= this.editor;
21412        
21413        var children = [];
21414        var btn = function(id,cmd , toggle, handler){
21415        
21416             var  event = toggle ? 'toggle' : 'click';
21417        
21418             var a = {
21419                 size : 'sm',
21420                 xtype: 'Button',
21421                 xns: Roo.bootstrap,
21422                 glyphicon : id,
21423                 cmd : id || cmd,
21424                 enableToggle:toggle !== false,
21425                 //html : 'submit'
21426                 pressed : toggle ? false : null,
21427                 listeners : {}
21428             };
21429             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21430                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21431             };
21432             children.push(a);
21433             return a;
21434        }
21435         
21436         var style = {
21437                 xtype: 'Button',
21438                 size : 'sm',
21439                 xns: Roo.bootstrap,
21440                 glyphicon : 'font',
21441                 //html : 'submit'
21442                 menu : {
21443                     xtype: 'Menu',
21444                     xns: Roo.bootstrap,
21445                     items:  []
21446                 }
21447         };
21448         Roo.each(this.formats, function(f) {
21449             style.menu.items.push({
21450                 xtype :'MenuItem',
21451                 xns: Roo.bootstrap,
21452                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21453                 tagname : f,
21454                 listeners : {
21455                     click : function()
21456                     {
21457                         editorcore.insertTag(this.tagname);
21458                         editor.focus();
21459                     }
21460                 }
21461                 
21462             });
21463         });
21464          children.push(style);   
21465             
21466             
21467         btn('bold',false,true);
21468         btn('italic',false,true);
21469         btn('align-left', 'justifyleft',true);
21470         btn('align-center', 'justifycenter',true);
21471         btn('align-right' , 'justifyright',true);
21472         btn('link', false, false, function(btn) {
21473             //Roo.log("create link?");
21474             var url = prompt(this.createLinkText, this.defaultLinkValue);
21475             if(url && url != 'http:/'+'/'){
21476                 this.editorcore.relayCmd('createlink', url);
21477             }
21478         }),
21479         btn('list','insertunorderedlist',true);
21480         btn('pencil', false,true, function(btn){
21481                 Roo.log(this);
21482                 
21483                 this.toggleSourceEdit(btn.pressed);
21484         });
21485         /*
21486         var cog = {
21487                 xtype: 'Button',
21488                 size : 'sm',
21489                 xns: Roo.bootstrap,
21490                 glyphicon : 'cog',
21491                 //html : 'submit'
21492                 menu : {
21493                     xtype: 'Menu',
21494                     xns: Roo.bootstrap,
21495                     items:  []
21496                 }
21497         };
21498         
21499         cog.menu.items.push({
21500             xtype :'MenuItem',
21501             xns: Roo.bootstrap,
21502             html : Clean styles,
21503             tagname : f,
21504             listeners : {
21505                 click : function()
21506                 {
21507                     editorcore.insertTag(this.tagname);
21508                     editor.focus();
21509                 }
21510             }
21511             
21512         });
21513        */
21514         
21515          
21516        this.xtype = 'NavSimplebar';
21517         
21518         for(var i=0;i< children.length;i++) {
21519             
21520             this.buttons.add(this.addxtypeChild(children[i]));
21521             
21522         }
21523         
21524         editor.on('editorevent', this.updateToolbar, this);
21525     },
21526     onBtnClick : function(id)
21527     {
21528        this.editorcore.relayCmd(id);
21529        this.editorcore.focus();
21530     },
21531     
21532     /**
21533      * Protected method that will not generally be called directly. It triggers
21534      * a toolbar update by reading the markup state of the current selection in the editor.
21535      */
21536     updateToolbar: function(){
21537
21538         if(!this.editorcore.activated){
21539             this.editor.onFirstFocus(); // is this neeed?
21540             return;
21541         }
21542
21543         var btns = this.buttons; 
21544         var doc = this.editorcore.doc;
21545         btns.get('bold').setActive(doc.queryCommandState('bold'));
21546         btns.get('italic').setActive(doc.queryCommandState('italic'));
21547         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21548         
21549         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21550         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21551         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21552         
21553         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21554         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21555          /*
21556         
21557         var ans = this.editorcore.getAllAncestors();
21558         if (this.formatCombo) {
21559             
21560             
21561             var store = this.formatCombo.store;
21562             this.formatCombo.setValue("");
21563             for (var i =0; i < ans.length;i++) {
21564                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21565                     // select it..
21566                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21567                     break;
21568                 }
21569             }
21570         }
21571         
21572         
21573         
21574         // hides menus... - so this cant be on a menu...
21575         Roo.bootstrap.MenuMgr.hideAll();
21576         */
21577         Roo.bootstrap.MenuMgr.hideAll();
21578         //this.editorsyncValue();
21579     },
21580     onFirstFocus: function() {
21581         this.buttons.each(function(item){
21582            item.enable();
21583         });
21584     },
21585     toggleSourceEdit : function(sourceEditMode){
21586         
21587           
21588         if(sourceEditMode){
21589             Roo.log("disabling buttons");
21590            this.buttons.each( function(item){
21591                 if(item.cmd != 'pencil'){
21592                     item.disable();
21593                 }
21594             });
21595           
21596         }else{
21597             Roo.log("enabling buttons");
21598             if(this.editorcore.initialized){
21599                 this.buttons.each( function(item){
21600                     item.enable();
21601                 });
21602             }
21603             
21604         }
21605         Roo.log("calling toggole on editor");
21606         // tell the editor that it's been pressed..
21607         this.editor.toggleSourceEdit(sourceEditMode);
21608        
21609     }
21610 });
21611
21612
21613
21614
21615
21616 /**
21617  * @class Roo.bootstrap.Table.AbstractSelectionModel
21618  * @extends Roo.util.Observable
21619  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21620  * implemented by descendant classes.  This class should not be directly instantiated.
21621  * @constructor
21622  */
21623 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21624     this.locked = false;
21625     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21626 };
21627
21628
21629 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21630     /** @ignore Called by the grid automatically. Do not call directly. */
21631     init : function(grid){
21632         this.grid = grid;
21633         this.initEvents();
21634     },
21635
21636     /**
21637      * Locks the selections.
21638      */
21639     lock : function(){
21640         this.locked = true;
21641     },
21642
21643     /**
21644      * Unlocks the selections.
21645      */
21646     unlock : function(){
21647         this.locked = false;
21648     },
21649
21650     /**
21651      * Returns true if the selections are locked.
21652      * @return {Boolean}
21653      */
21654     isLocked : function(){
21655         return this.locked;
21656     }
21657 });
21658 /**
21659  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21660  * @class Roo.bootstrap.Table.RowSelectionModel
21661  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21662  * It supports multiple selections and keyboard selection/navigation. 
21663  * @constructor
21664  * @param {Object} config
21665  */
21666
21667 Roo.bootstrap.Table.RowSelectionModel = function(config){
21668     Roo.apply(this, config);
21669     this.selections = new Roo.util.MixedCollection(false, function(o){
21670         return o.id;
21671     });
21672
21673     this.last = false;
21674     this.lastActive = false;
21675
21676     this.addEvents({
21677         /**
21678              * @event selectionchange
21679              * Fires when the selection changes
21680              * @param {SelectionModel} this
21681              */
21682             "selectionchange" : true,
21683         /**
21684              * @event afterselectionchange
21685              * Fires after the selection changes (eg. by key press or clicking)
21686              * @param {SelectionModel} this
21687              */
21688             "afterselectionchange" : true,
21689         /**
21690              * @event beforerowselect
21691              * Fires when a row is selected being selected, return false to cancel.
21692              * @param {SelectionModel} this
21693              * @param {Number} rowIndex The selected index
21694              * @param {Boolean} keepExisting False if other selections will be cleared
21695              */
21696             "beforerowselect" : true,
21697         /**
21698              * @event rowselect
21699              * Fires when a row is selected.
21700              * @param {SelectionModel} this
21701              * @param {Number} rowIndex The selected index
21702              * @param {Roo.data.Record} r The record
21703              */
21704             "rowselect" : true,
21705         /**
21706              * @event rowdeselect
21707              * Fires when a row is deselected.
21708              * @param {SelectionModel} this
21709              * @param {Number} rowIndex The selected index
21710              */
21711         "rowdeselect" : true
21712     });
21713     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21714     this.locked = false;
21715 };
21716
21717 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21718     /**
21719      * @cfg {Boolean} singleSelect
21720      * True to allow selection of only one row at a time (defaults to false)
21721      */
21722     singleSelect : false,
21723
21724     // private
21725     initEvents : function(){
21726
21727         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21728             this.grid.on("mousedown", this.handleMouseDown, this);
21729         }else{ // allow click to work like normal
21730             this.grid.on("rowclick", this.handleDragableRowClick, this);
21731         }
21732
21733         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21734             "up" : function(e){
21735                 if(!e.shiftKey){
21736                     this.selectPrevious(e.shiftKey);
21737                 }else if(this.last !== false && this.lastActive !== false){
21738                     var last = this.last;
21739                     this.selectRange(this.last,  this.lastActive-1);
21740                     this.grid.getView().focusRow(this.lastActive);
21741                     if(last !== false){
21742                         this.last = last;
21743                     }
21744                 }else{
21745                     this.selectFirstRow();
21746                 }
21747                 this.fireEvent("afterselectionchange", this);
21748             },
21749             "down" : function(e){
21750                 if(!e.shiftKey){
21751                     this.selectNext(e.shiftKey);
21752                 }else if(this.last !== false && this.lastActive !== false){
21753                     var last = this.last;
21754                     this.selectRange(this.last,  this.lastActive+1);
21755                     this.grid.getView().focusRow(this.lastActive);
21756                     if(last !== false){
21757                         this.last = last;
21758                     }
21759                 }else{
21760                     this.selectFirstRow();
21761                 }
21762                 this.fireEvent("afterselectionchange", this);
21763             },
21764             scope: this
21765         });
21766
21767         var view = this.grid.view;
21768         view.on("refresh", this.onRefresh, this);
21769         view.on("rowupdated", this.onRowUpdated, this);
21770         view.on("rowremoved", this.onRemove, this);
21771     },
21772
21773     // private
21774     onRefresh : function(){
21775         var ds = this.grid.dataSource, i, v = this.grid.view;
21776         var s = this.selections;
21777         s.each(function(r){
21778             if((i = ds.indexOfId(r.id)) != -1){
21779                 v.onRowSelect(i);
21780             }else{
21781                 s.remove(r);
21782             }
21783         });
21784     },
21785
21786     // private
21787     onRemove : function(v, index, r){
21788         this.selections.remove(r);
21789     },
21790
21791     // private
21792     onRowUpdated : function(v, index, r){
21793         if(this.isSelected(r)){
21794             v.onRowSelect(index);
21795         }
21796     },
21797
21798     /**
21799      * Select records.
21800      * @param {Array} records The records to select
21801      * @param {Boolean} keepExisting (optional) True to keep existing selections
21802      */
21803     selectRecords : function(records, keepExisting){
21804         if(!keepExisting){
21805             this.clearSelections();
21806         }
21807         var ds = this.grid.dataSource;
21808         for(var i = 0, len = records.length; i < len; i++){
21809             this.selectRow(ds.indexOf(records[i]), true);
21810         }
21811     },
21812
21813     /**
21814      * Gets the number of selected rows.
21815      * @return {Number}
21816      */
21817     getCount : function(){
21818         return this.selections.length;
21819     },
21820
21821     /**
21822      * Selects the first row in the grid.
21823      */
21824     selectFirstRow : function(){
21825         this.selectRow(0);
21826     },
21827
21828     /**
21829      * Select the last row.
21830      * @param {Boolean} keepExisting (optional) True to keep existing selections
21831      */
21832     selectLastRow : function(keepExisting){
21833         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21834     },
21835
21836     /**
21837      * Selects the row immediately following the last selected row.
21838      * @param {Boolean} keepExisting (optional) True to keep existing selections
21839      */
21840     selectNext : function(keepExisting){
21841         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21842             this.selectRow(this.last+1, keepExisting);
21843             this.grid.getView().focusRow(this.last);
21844         }
21845     },
21846
21847     /**
21848      * Selects the row that precedes the last selected row.
21849      * @param {Boolean} keepExisting (optional) True to keep existing selections
21850      */
21851     selectPrevious : function(keepExisting){
21852         if(this.last){
21853             this.selectRow(this.last-1, keepExisting);
21854             this.grid.getView().focusRow(this.last);
21855         }
21856     },
21857
21858     /**
21859      * Returns the selected records
21860      * @return {Array} Array of selected records
21861      */
21862     getSelections : function(){
21863         return [].concat(this.selections.items);
21864     },
21865
21866     /**
21867      * Returns the first selected record.
21868      * @return {Record}
21869      */
21870     getSelected : function(){
21871         return this.selections.itemAt(0);
21872     },
21873
21874
21875     /**
21876      * Clears all selections.
21877      */
21878     clearSelections : function(fast){
21879         if(this.locked) {
21880             return;
21881         }
21882         if(fast !== true){
21883             var ds = this.grid.dataSource;
21884             var s = this.selections;
21885             s.each(function(r){
21886                 this.deselectRow(ds.indexOfId(r.id));
21887             }, this);
21888             s.clear();
21889         }else{
21890             this.selections.clear();
21891         }
21892         this.last = false;
21893     },
21894
21895
21896     /**
21897      * Selects all rows.
21898      */
21899     selectAll : function(){
21900         if(this.locked) {
21901             return;
21902         }
21903         this.selections.clear();
21904         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21905             this.selectRow(i, true);
21906         }
21907     },
21908
21909     /**
21910      * Returns True if there is a selection.
21911      * @return {Boolean}
21912      */
21913     hasSelection : function(){
21914         return this.selections.length > 0;
21915     },
21916
21917     /**
21918      * Returns True if the specified row is selected.
21919      * @param {Number/Record} record The record or index of the record to check
21920      * @return {Boolean}
21921      */
21922     isSelected : function(index){
21923         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21924         return (r && this.selections.key(r.id) ? true : false);
21925     },
21926
21927     /**
21928      * Returns True if the specified record id is selected.
21929      * @param {String} id The id of record to check
21930      * @return {Boolean}
21931      */
21932     isIdSelected : function(id){
21933         return (this.selections.key(id) ? true : false);
21934     },
21935
21936     // private
21937     handleMouseDown : function(e, t){
21938         var view = this.grid.getView(), rowIndex;
21939         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21940             return;
21941         };
21942         if(e.shiftKey && this.last !== false){
21943             var last = this.last;
21944             this.selectRange(last, rowIndex, e.ctrlKey);
21945             this.last = last; // reset the last
21946             view.focusRow(rowIndex);
21947         }else{
21948             var isSelected = this.isSelected(rowIndex);
21949             if(e.button !== 0 && isSelected){
21950                 view.focusRow(rowIndex);
21951             }else if(e.ctrlKey && isSelected){
21952                 this.deselectRow(rowIndex);
21953             }else if(!isSelected){
21954                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21955                 view.focusRow(rowIndex);
21956             }
21957         }
21958         this.fireEvent("afterselectionchange", this);
21959     },
21960     // private
21961     handleDragableRowClick :  function(grid, rowIndex, e) 
21962     {
21963         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21964             this.selectRow(rowIndex, false);
21965             grid.view.focusRow(rowIndex);
21966              this.fireEvent("afterselectionchange", this);
21967         }
21968     },
21969     
21970     /**
21971      * Selects multiple rows.
21972      * @param {Array} rows Array of the indexes of the row to select
21973      * @param {Boolean} keepExisting (optional) True to keep existing selections
21974      */
21975     selectRows : function(rows, keepExisting){
21976         if(!keepExisting){
21977             this.clearSelections();
21978         }
21979         for(var i = 0, len = rows.length; i < len; i++){
21980             this.selectRow(rows[i], true);
21981         }
21982     },
21983
21984     /**
21985      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21986      * @param {Number} startRow The index of the first row in the range
21987      * @param {Number} endRow The index of the last row in the range
21988      * @param {Boolean} keepExisting (optional) True to retain existing selections
21989      */
21990     selectRange : function(startRow, endRow, keepExisting){
21991         if(this.locked) {
21992             return;
21993         }
21994         if(!keepExisting){
21995             this.clearSelections();
21996         }
21997         if(startRow <= endRow){
21998             for(var i = startRow; i <= endRow; i++){
21999                 this.selectRow(i, true);
22000             }
22001         }else{
22002             for(var i = startRow; i >= endRow; i--){
22003                 this.selectRow(i, true);
22004             }
22005         }
22006     },
22007
22008     /**
22009      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22010      * @param {Number} startRow The index of the first row in the range
22011      * @param {Number} endRow The index of the last row in the range
22012      */
22013     deselectRange : function(startRow, endRow, preventViewNotify){
22014         if(this.locked) {
22015             return;
22016         }
22017         for(var i = startRow; i <= endRow; i++){
22018             this.deselectRow(i, preventViewNotify);
22019         }
22020     },
22021
22022     /**
22023      * Selects a row.
22024      * @param {Number} row The index of the row to select
22025      * @param {Boolean} keepExisting (optional) True to keep existing selections
22026      */
22027     selectRow : function(index, keepExisting, preventViewNotify){
22028         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22029             return;
22030         }
22031         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22032             if(!keepExisting || this.singleSelect){
22033                 this.clearSelections();
22034             }
22035             var r = this.grid.dataSource.getAt(index);
22036             this.selections.add(r);
22037             this.last = this.lastActive = index;
22038             if(!preventViewNotify){
22039                 this.grid.getView().onRowSelect(index);
22040             }
22041             this.fireEvent("rowselect", this, index, r);
22042             this.fireEvent("selectionchange", this);
22043         }
22044     },
22045
22046     /**
22047      * Deselects a row.
22048      * @param {Number} row The index of the row to deselect
22049      */
22050     deselectRow : function(index, preventViewNotify){
22051         if(this.locked) {
22052             return;
22053         }
22054         if(this.last == index){
22055             this.last = false;
22056         }
22057         if(this.lastActive == index){
22058             this.lastActive = false;
22059         }
22060         var r = this.grid.dataSource.getAt(index);
22061         this.selections.remove(r);
22062         if(!preventViewNotify){
22063             this.grid.getView().onRowDeselect(index);
22064         }
22065         this.fireEvent("rowdeselect", this, index);
22066         this.fireEvent("selectionchange", this);
22067     },
22068
22069     // private
22070     restoreLast : function(){
22071         if(this._last){
22072             this.last = this._last;
22073         }
22074     },
22075
22076     // private
22077     acceptsNav : function(row, col, cm){
22078         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22079     },
22080
22081     // private
22082     onEditorKey : function(field, e){
22083         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22084         if(k == e.TAB){
22085             e.stopEvent();
22086             ed.completeEdit();
22087             if(e.shiftKey){
22088                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22089             }else{
22090                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22091             }
22092         }else if(k == e.ENTER && !e.ctrlKey){
22093             e.stopEvent();
22094             ed.completeEdit();
22095             if(e.shiftKey){
22096                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22097             }else{
22098                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22099             }
22100         }else if(k == e.ESC){
22101             ed.cancelEdit();
22102         }
22103         if(newCell){
22104             g.startEditing(newCell[0], newCell[1]);
22105         }
22106     }
22107 });/*
22108  * Based on:
22109  * Ext JS Library 1.1.1
22110  * Copyright(c) 2006-2007, Ext JS, LLC.
22111  *
22112  * Originally Released Under LGPL - original licence link has changed is not relivant.
22113  *
22114  * Fork - LGPL
22115  * <script type="text/javascript">
22116  */
22117  
22118 /**
22119  * @class Roo.bootstrap.PagingToolbar
22120  * @extends Roo.bootstrap.NavSimplebar
22121  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22122  * @constructor
22123  * Create a new PagingToolbar
22124  * @param {Object} config The config object
22125  * @param {Roo.data.Store} store
22126  */
22127 Roo.bootstrap.PagingToolbar = function(config)
22128 {
22129     // old args format still supported... - xtype is prefered..
22130         // created from xtype...
22131     
22132     this.ds = config.dataSource;
22133     
22134     if (config.store && !this.ds) {
22135         this.store= Roo.factory(config.store, Roo.data);
22136         this.ds = this.store;
22137         this.ds.xmodule = this.xmodule || false;
22138     }
22139     
22140     this.toolbarItems = [];
22141     if (config.items) {
22142         this.toolbarItems = config.items;
22143     }
22144     
22145     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22146     
22147     this.cursor = 0;
22148     
22149     if (this.ds) { 
22150         this.bind(this.ds);
22151     }
22152     
22153     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22154     
22155 };
22156
22157 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22158     /**
22159      * @cfg {Roo.data.Store} dataSource
22160      * The underlying data store providing the paged data
22161      */
22162     /**
22163      * @cfg {String/HTMLElement/Element} container
22164      * container The id or element that will contain the toolbar
22165      */
22166     /**
22167      * @cfg {Boolean} displayInfo
22168      * True to display the displayMsg (defaults to false)
22169      */
22170     /**
22171      * @cfg {Number} pageSize
22172      * The number of records to display per page (defaults to 20)
22173      */
22174     pageSize: 20,
22175     /**
22176      * @cfg {String} displayMsg
22177      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22178      */
22179     displayMsg : 'Displaying {0} - {1} of {2}',
22180     /**
22181      * @cfg {String} emptyMsg
22182      * The message to display when no records are found (defaults to "No data to display")
22183      */
22184     emptyMsg : 'No data to display',
22185     /**
22186      * Customizable piece of the default paging text (defaults to "Page")
22187      * @type String
22188      */
22189     beforePageText : "Page",
22190     /**
22191      * Customizable piece of the default paging text (defaults to "of %0")
22192      * @type String
22193      */
22194     afterPageText : "of {0}",
22195     /**
22196      * Customizable piece of the default paging text (defaults to "First Page")
22197      * @type String
22198      */
22199     firstText : "First Page",
22200     /**
22201      * Customizable piece of the default paging text (defaults to "Previous Page")
22202      * @type String
22203      */
22204     prevText : "Previous Page",
22205     /**
22206      * Customizable piece of the default paging text (defaults to "Next Page")
22207      * @type String
22208      */
22209     nextText : "Next Page",
22210     /**
22211      * Customizable piece of the default paging text (defaults to "Last Page")
22212      * @type String
22213      */
22214     lastText : "Last Page",
22215     /**
22216      * Customizable piece of the default paging text (defaults to "Refresh")
22217      * @type String
22218      */
22219     refreshText : "Refresh",
22220
22221     buttons : false,
22222     // private
22223     onRender : function(ct, position) 
22224     {
22225         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22226         this.navgroup.parentId = this.id;
22227         this.navgroup.onRender(this.el, null);
22228         // add the buttons to the navgroup
22229         
22230         if(this.displayInfo){
22231             Roo.log(this.el.select('ul.navbar-nav',true).first());
22232             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22233             this.displayEl = this.el.select('.x-paging-info', true).first();
22234 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22235 //            this.displayEl = navel.el.select('span',true).first();
22236         }
22237         
22238         var _this = this;
22239         
22240         if(this.buttons){
22241             Roo.each(_this.buttons, function(e){ // this might need to use render????
22242                Roo.factory(e).onRender(_this.el, null);
22243             });
22244         }
22245             
22246         Roo.each(_this.toolbarItems, function(e) {
22247             _this.navgroup.addItem(e);
22248         });
22249         
22250         
22251         this.first = this.navgroup.addItem({
22252             tooltip: this.firstText,
22253             cls: "prev",
22254             icon : 'fa fa-backward',
22255             disabled: true,
22256             preventDefault: true,
22257             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22258         });
22259         
22260         this.prev =  this.navgroup.addItem({
22261             tooltip: this.prevText,
22262             cls: "prev",
22263             icon : 'fa fa-step-backward',
22264             disabled: true,
22265             preventDefault: true,
22266             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22267         });
22268     //this.addSeparator();
22269         
22270         
22271         var field = this.navgroup.addItem( {
22272             tagtype : 'span',
22273             cls : 'x-paging-position',
22274             
22275             html : this.beforePageText  +
22276                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22277                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22278          } ); //?? escaped?
22279         
22280         this.field = field.el.select('input', true).first();
22281         this.field.on("keydown", this.onPagingKeydown, this);
22282         this.field.on("focus", function(){this.dom.select();});
22283     
22284     
22285         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22286         //this.field.setHeight(18);
22287         //this.addSeparator();
22288         this.next = this.navgroup.addItem({
22289             tooltip: this.nextText,
22290             cls: "next",
22291             html : ' <i class="fa fa-step-forward">',
22292             disabled: true,
22293             preventDefault: true,
22294             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22295         });
22296         this.last = this.navgroup.addItem({
22297             tooltip: this.lastText,
22298             icon : 'fa fa-forward',
22299             cls: "next",
22300             disabled: true,
22301             preventDefault: true,
22302             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22303         });
22304     //this.addSeparator();
22305         this.loading = this.navgroup.addItem({
22306             tooltip: this.refreshText,
22307             icon: 'fa fa-refresh',
22308             preventDefault: true,
22309             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22310         });
22311         
22312     },
22313
22314     // private
22315     updateInfo : function(){
22316         if(this.displayEl){
22317             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22318             var msg = count == 0 ?
22319                 this.emptyMsg :
22320                 String.format(
22321                     this.displayMsg,
22322                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22323                 );
22324             this.displayEl.update(msg);
22325         }
22326     },
22327
22328     // private
22329     onLoad : function(ds, r, o){
22330        this.cursor = o.params ? o.params.start : 0;
22331        var d = this.getPageData(),
22332             ap = d.activePage,
22333             ps = d.pages;
22334         
22335        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22336        this.field.dom.value = ap;
22337        this.first.setDisabled(ap == 1);
22338        this.prev.setDisabled(ap == 1);
22339        this.next.setDisabled(ap == ps);
22340        this.last.setDisabled(ap == ps);
22341        this.loading.enable();
22342        this.updateInfo();
22343     },
22344
22345     // private
22346     getPageData : function(){
22347         var total = this.ds.getTotalCount();
22348         return {
22349             total : total,
22350             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22351             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22352         };
22353     },
22354
22355     // private
22356     onLoadError : function(){
22357         this.loading.enable();
22358     },
22359
22360     // private
22361     onPagingKeydown : function(e){
22362         var k = e.getKey();
22363         var d = this.getPageData();
22364         if(k == e.RETURN){
22365             var v = this.field.dom.value, pageNum;
22366             if(!v || isNaN(pageNum = parseInt(v, 10))){
22367                 this.field.dom.value = d.activePage;
22368                 return;
22369             }
22370             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22371             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22372             e.stopEvent();
22373         }
22374         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))
22375         {
22376           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22377           this.field.dom.value = pageNum;
22378           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22379           e.stopEvent();
22380         }
22381         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22382         {
22383           var v = this.field.dom.value, pageNum; 
22384           var increment = (e.shiftKey) ? 10 : 1;
22385           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22386                 increment *= -1;
22387           }
22388           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22389             this.field.dom.value = d.activePage;
22390             return;
22391           }
22392           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22393           {
22394             this.field.dom.value = parseInt(v, 10) + increment;
22395             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22396             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22397           }
22398           e.stopEvent();
22399         }
22400     },
22401
22402     // private
22403     beforeLoad : function(){
22404         if(this.loading){
22405             this.loading.disable();
22406         }
22407     },
22408
22409     // private
22410     onClick : function(which){
22411         
22412         var ds = this.ds;
22413         if (!ds) {
22414             return;
22415         }
22416         
22417         switch(which){
22418             case "first":
22419                 ds.load({params:{start: 0, limit: this.pageSize}});
22420             break;
22421             case "prev":
22422                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22423             break;
22424             case "next":
22425                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22426             break;
22427             case "last":
22428                 var total = ds.getTotalCount();
22429                 var extra = total % this.pageSize;
22430                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22431                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22432             break;
22433             case "refresh":
22434                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22435             break;
22436         }
22437     },
22438
22439     /**
22440      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22441      * @param {Roo.data.Store} store The data store to unbind
22442      */
22443     unbind : function(ds){
22444         ds.un("beforeload", this.beforeLoad, this);
22445         ds.un("load", this.onLoad, this);
22446         ds.un("loadexception", this.onLoadError, this);
22447         ds.un("remove", this.updateInfo, this);
22448         ds.un("add", this.updateInfo, this);
22449         this.ds = undefined;
22450     },
22451
22452     /**
22453      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22454      * @param {Roo.data.Store} store The data store to bind
22455      */
22456     bind : function(ds){
22457         ds.on("beforeload", this.beforeLoad, this);
22458         ds.on("load", this.onLoad, this);
22459         ds.on("loadexception", this.onLoadError, this);
22460         ds.on("remove", this.updateInfo, this);
22461         ds.on("add", this.updateInfo, this);
22462         this.ds = ds;
22463     }
22464 });/*
22465  * - LGPL
22466  *
22467  * element
22468  * 
22469  */
22470
22471 /**
22472  * @class Roo.bootstrap.MessageBar
22473  * @extends Roo.bootstrap.Component
22474  * Bootstrap MessageBar class
22475  * @cfg {String} html contents of the MessageBar
22476  * @cfg {String} weight (info | success | warning | danger) default info
22477  * @cfg {String} beforeClass insert the bar before the given class
22478  * @cfg {Boolean} closable (true | false) default false
22479  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22480  * 
22481  * @constructor
22482  * Create a new Element
22483  * @param {Object} config The config object
22484  */
22485
22486 Roo.bootstrap.MessageBar = function(config){
22487     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22488 };
22489
22490 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22491     
22492     html: '',
22493     weight: 'info',
22494     closable: false,
22495     fixed: false,
22496     beforeClass: 'bootstrap-sticky-wrap',
22497     
22498     getAutoCreate : function(){
22499         
22500         var cfg = {
22501             tag: 'div',
22502             cls: 'alert alert-dismissable alert-' + this.weight,
22503             cn: [
22504                 {
22505                     tag: 'span',
22506                     cls: 'message',
22507                     html: this.html || ''
22508                 }
22509             ]
22510         };
22511         
22512         if(this.fixed){
22513             cfg.cls += ' alert-messages-fixed';
22514         }
22515         
22516         if(this.closable){
22517             cfg.cn.push({
22518                 tag: 'button',
22519                 cls: 'close',
22520                 html: 'x'
22521             });
22522         }
22523         
22524         return cfg;
22525     },
22526     
22527     onRender : function(ct, position)
22528     {
22529         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22530         
22531         if(!this.el){
22532             var cfg = Roo.apply({},  this.getAutoCreate());
22533             cfg.id = Roo.id();
22534             
22535             if (this.cls) {
22536                 cfg.cls += ' ' + this.cls;
22537             }
22538             if (this.style) {
22539                 cfg.style = this.style;
22540             }
22541             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22542             
22543             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22544         }
22545         
22546         this.el.select('>button.close').on('click', this.hide, this);
22547         
22548     },
22549     
22550     show : function()
22551     {
22552         if (!this.rendered) {
22553             this.render();
22554         }
22555         
22556         this.el.show();
22557         
22558         this.fireEvent('show', this);
22559         
22560     },
22561     
22562     hide : function()
22563     {
22564         if (!this.rendered) {
22565             this.render();
22566         }
22567         
22568         this.el.hide();
22569         
22570         this.fireEvent('hide', this);
22571     },
22572     
22573     update : function()
22574     {
22575 //        var e = this.el.dom.firstChild;
22576 //        
22577 //        if(this.closable){
22578 //            e = e.nextSibling;
22579 //        }
22580 //        
22581 //        e.data = this.html || '';
22582
22583         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22584     }
22585    
22586 });
22587
22588  
22589
22590      /*
22591  * - LGPL
22592  *
22593  * Graph
22594  * 
22595  */
22596
22597
22598 /**
22599  * @class Roo.bootstrap.Graph
22600  * @extends Roo.bootstrap.Component
22601  * Bootstrap Graph class
22602 > Prameters
22603  -sm {number} sm 4
22604  -md {number} md 5
22605  @cfg {String} graphtype  bar | vbar | pie
22606  @cfg {number} g_x coodinator | centre x (pie)
22607  @cfg {number} g_y coodinator | centre y (pie)
22608  @cfg {number} g_r radius (pie)
22609  @cfg {number} g_height height of the chart (respected by all elements in the set)
22610  @cfg {number} g_width width of the chart (respected by all elements in the set)
22611  @cfg {Object} title The title of the chart
22612     
22613  -{Array}  values
22614  -opts (object) options for the chart 
22615      o {
22616      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22617      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22618      o vgutter (number)
22619      o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
22620      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22621      o to
22622      o stretch (boolean)
22623      o }
22624  -opts (object) options for the pie
22625      o{
22626      o cut
22627      o startAngle (number)
22628      o endAngle (number)
22629      } 
22630  *
22631  * @constructor
22632  * Create a new Input
22633  * @param {Object} config The config object
22634  */
22635
22636 Roo.bootstrap.Graph = function(config){
22637     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22638     
22639     this.addEvents({
22640         // img events
22641         /**
22642          * @event click
22643          * The img click event for the img.
22644          * @param {Roo.EventObject} e
22645          */
22646         "click" : true
22647     });
22648 };
22649
22650 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22651     
22652     sm: 4,
22653     md: 5,
22654     graphtype: 'bar',
22655     g_height: 250,
22656     g_width: 400,
22657     g_x: 50,
22658     g_y: 50,
22659     g_r: 30,
22660     opts:{
22661         //g_colors: this.colors,
22662         g_type: 'soft',
22663         g_gutter: '20%'
22664
22665     },
22666     title : false,
22667
22668     getAutoCreate : function(){
22669         
22670         var cfg = {
22671             tag: 'div',
22672             html : null
22673         };
22674         
22675         
22676         return  cfg;
22677     },
22678
22679     onRender : function(ct,position){
22680         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22681         this.raphael = Raphael(this.el.dom);
22682         
22683                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22684                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22685                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22686                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22687                 /*
22688                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22689                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22690                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22691                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22692                 
22693                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22694                 r.barchart(330, 10, 300, 220, data1);
22695                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22696                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22697                 */
22698                 
22699                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22700                 // r.barchart(30, 30, 560, 250,  xdata, {
22701                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22702                 //     axis : "0 0 1 1",
22703                 //     axisxlabels :  xdata
22704                 //     //yvalues : cols,
22705                    
22706                 // });
22707 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22708 //        
22709 //        this.load(null,xdata,{
22710 //                axis : "0 0 1 1",
22711 //                axisxlabels :  xdata
22712 //                });
22713
22714     },
22715
22716     load : function(graphtype,xdata,opts){
22717         this.raphael.clear();
22718         if(!graphtype) {
22719             graphtype = this.graphtype;
22720         }
22721         if(!opts){
22722             opts = this.opts;
22723         }
22724         var r = this.raphael,
22725             fin = function () {
22726                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22727             },
22728             fout = function () {
22729                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22730             },
22731             pfin = function() {
22732                 this.sector.stop();
22733                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22734
22735                 if (this.label) {
22736                     this.label[0].stop();
22737                     this.label[0].attr({ r: 7.5 });
22738                     this.label[1].attr({ "font-weight": 800 });
22739                 }
22740             },
22741             pfout = function() {
22742                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22743
22744                 if (this.label) {
22745                     this.label[0].animate({ r: 5 }, 500, "bounce");
22746                     this.label[1].attr({ "font-weight": 400 });
22747                 }
22748             };
22749
22750         switch(graphtype){
22751             case 'bar':
22752                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22753                 break;
22754             case 'hbar':
22755                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22756                 break;
22757             case 'pie':
22758 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22759 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22760 //            
22761                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22762                 
22763                 break;
22764
22765         }
22766         
22767         if(this.title){
22768             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22769         }
22770         
22771     },
22772     
22773     setTitle: function(o)
22774     {
22775         this.title = o;
22776     },
22777     
22778     initEvents: function() {
22779         
22780         if(!this.href){
22781             this.el.on('click', this.onClick, this);
22782         }
22783     },
22784     
22785     onClick : function(e)
22786     {
22787         Roo.log('img onclick');
22788         this.fireEvent('click', this, e);
22789     }
22790    
22791 });
22792
22793  
22794 /*
22795  * - LGPL
22796  *
22797  * numberBox
22798  * 
22799  */
22800 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22801
22802 /**
22803  * @class Roo.bootstrap.dash.NumberBox
22804  * @extends Roo.bootstrap.Component
22805  * Bootstrap NumberBox class
22806  * @cfg {String} headline Box headline
22807  * @cfg {String} content Box content
22808  * @cfg {String} icon Box icon
22809  * @cfg {String} footer Footer text
22810  * @cfg {String} fhref Footer href
22811  * 
22812  * @constructor
22813  * Create a new NumberBox
22814  * @param {Object} config The config object
22815  */
22816
22817
22818 Roo.bootstrap.dash.NumberBox = function(config){
22819     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22820     
22821 };
22822
22823 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22824     
22825     headline : '',
22826     content : '',
22827     icon : '',
22828     footer : '',
22829     fhref : '',
22830     ficon : '',
22831     
22832     getAutoCreate : function(){
22833         
22834         var cfg = {
22835             tag : 'div',
22836             cls : 'small-box ',
22837             cn : [
22838                 {
22839                     tag : 'div',
22840                     cls : 'inner',
22841                     cn :[
22842                         {
22843                             tag : 'h3',
22844                             cls : 'roo-headline',
22845                             html : this.headline
22846                         },
22847                         {
22848                             tag : 'p',
22849                             cls : 'roo-content',
22850                             html : this.content
22851                         }
22852                     ]
22853                 }
22854             ]
22855         };
22856         
22857         if(this.icon){
22858             cfg.cn.push({
22859                 tag : 'div',
22860                 cls : 'icon',
22861                 cn :[
22862                     {
22863                         tag : 'i',
22864                         cls : 'ion ' + this.icon
22865                     }
22866                 ]
22867             });
22868         }
22869         
22870         if(this.footer){
22871             var footer = {
22872                 tag : 'a',
22873                 cls : 'small-box-footer',
22874                 href : this.fhref || '#',
22875                 html : this.footer
22876             };
22877             
22878             cfg.cn.push(footer);
22879             
22880         }
22881         
22882         return  cfg;
22883     },
22884
22885     onRender : function(ct,position){
22886         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22887
22888
22889        
22890                 
22891     },
22892
22893     setHeadline: function (value)
22894     {
22895         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22896     },
22897     
22898     setFooter: function (value, href)
22899     {
22900         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22901         
22902         if(href){
22903             this.el.select('a.small-box-footer',true).first().attr('href', href);
22904         }
22905         
22906     },
22907
22908     setContent: function (value)
22909     {
22910         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22911     },
22912
22913     initEvents: function() 
22914     {   
22915         
22916     }
22917     
22918 });
22919
22920  
22921 /*
22922  * - LGPL
22923  *
22924  * TabBox
22925  * 
22926  */
22927 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22928
22929 /**
22930  * @class Roo.bootstrap.dash.TabBox
22931  * @extends Roo.bootstrap.Component
22932  * Bootstrap TabBox class
22933  * @cfg {String} title Title of the TabBox
22934  * @cfg {String} icon Icon of the TabBox
22935  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22936  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22937  * 
22938  * @constructor
22939  * Create a new TabBox
22940  * @param {Object} config The config object
22941  */
22942
22943
22944 Roo.bootstrap.dash.TabBox = function(config){
22945     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22946     this.addEvents({
22947         // raw events
22948         /**
22949          * @event addpane
22950          * When a pane is added
22951          * @param {Roo.bootstrap.dash.TabPane} pane
22952          */
22953         "addpane" : true,
22954         /**
22955          * @event activatepane
22956          * When a pane is activated
22957          * @param {Roo.bootstrap.dash.TabPane} pane
22958          */
22959         "activatepane" : true
22960         
22961          
22962     });
22963     
22964     this.panes = [];
22965 };
22966
22967 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22968
22969     title : '',
22970     icon : false,
22971     showtabs : true,
22972     tabScrollable : false,
22973     
22974     getChildContainer : function()
22975     {
22976         return this.el.select('.tab-content', true).first();
22977     },
22978     
22979     getAutoCreate : function(){
22980         
22981         var header = {
22982             tag: 'li',
22983             cls: 'pull-left header',
22984             html: this.title,
22985             cn : []
22986         };
22987         
22988         if(this.icon){
22989             header.cn.push({
22990                 tag: 'i',
22991                 cls: 'fa ' + this.icon
22992             });
22993         }
22994         
22995         var h = {
22996             tag: 'ul',
22997             cls: 'nav nav-tabs pull-right',
22998             cn: [
22999                 header
23000             ]
23001         };
23002         
23003         if(this.tabScrollable){
23004             h = {
23005                 tag: 'div',
23006                 cls: 'tab-header',
23007                 cn: [
23008                     {
23009                         tag: 'ul',
23010                         cls: 'nav nav-tabs pull-right',
23011                         cn: [
23012                             header
23013                         ]
23014                     }
23015                 ]
23016             };
23017         }
23018         
23019         var cfg = {
23020             tag: 'div',
23021             cls: 'nav-tabs-custom',
23022             cn: [
23023                 h,
23024                 {
23025                     tag: 'div',
23026                     cls: 'tab-content no-padding',
23027                     cn: []
23028                 }
23029             ]
23030         };
23031
23032         return  cfg;
23033     },
23034     initEvents : function()
23035     {
23036         //Roo.log('add add pane handler');
23037         this.on('addpane', this.onAddPane, this);
23038     },
23039      /**
23040      * Updates the box title
23041      * @param {String} html to set the title to.
23042      */
23043     setTitle : function(value)
23044     {
23045         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23046     },
23047     onAddPane : function(pane)
23048     {
23049         this.panes.push(pane);
23050         //Roo.log('addpane');
23051         //Roo.log(pane);
23052         // tabs are rendere left to right..
23053         if(!this.showtabs){
23054             return;
23055         }
23056         
23057         var ctr = this.el.select('.nav-tabs', true).first();
23058          
23059          
23060         var existing = ctr.select('.nav-tab',true);
23061         var qty = existing.getCount();;
23062         
23063         
23064         var tab = ctr.createChild({
23065             tag : 'li',
23066             cls : 'nav-tab' + (qty ? '' : ' active'),
23067             cn : [
23068                 {
23069                     tag : 'a',
23070                     href:'#',
23071                     html : pane.title
23072                 }
23073             ]
23074         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23075         pane.tab = tab;
23076         
23077         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23078         if (!qty) {
23079             pane.el.addClass('active');
23080         }
23081         
23082                 
23083     },
23084     onTabClick : function(ev,un,ob,pane)
23085     {
23086         //Roo.log('tab - prev default');
23087         ev.preventDefault();
23088         
23089         
23090         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23091         pane.tab.addClass('active');
23092         //Roo.log(pane.title);
23093         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23094         // technically we should have a deactivate event.. but maybe add later.
23095         // and it should not de-activate the selected tab...
23096         this.fireEvent('activatepane', pane);
23097         pane.el.addClass('active');
23098         pane.fireEvent('activate');
23099         
23100         
23101     },
23102     
23103     getActivePane : function()
23104     {
23105         var r = false;
23106         Roo.each(this.panes, function(p) {
23107             if(p.el.hasClass('active')){
23108                 r = p;
23109                 return false;
23110             }
23111             
23112             return;
23113         });
23114         
23115         return r;
23116     }
23117     
23118     
23119 });
23120
23121  
23122 /*
23123  * - LGPL
23124  *
23125  * Tab pane
23126  * 
23127  */
23128 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23129 /**
23130  * @class Roo.bootstrap.TabPane
23131  * @extends Roo.bootstrap.Component
23132  * Bootstrap TabPane class
23133  * @cfg {Boolean} active (false | true) Default false
23134  * @cfg {String} title title of panel
23135
23136  * 
23137  * @constructor
23138  * Create a new TabPane
23139  * @param {Object} config The config object
23140  */
23141
23142 Roo.bootstrap.dash.TabPane = function(config){
23143     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23144     
23145     this.addEvents({
23146         // raw events
23147         /**
23148          * @event activate
23149          * When a pane is activated
23150          * @param {Roo.bootstrap.dash.TabPane} pane
23151          */
23152         "activate" : true
23153          
23154     });
23155 };
23156
23157 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23158     
23159     active : false,
23160     title : '',
23161     
23162     // the tabBox that this is attached to.
23163     tab : false,
23164      
23165     getAutoCreate : function() 
23166     {
23167         var cfg = {
23168             tag: 'div',
23169             cls: 'tab-pane'
23170         };
23171         
23172         if(this.active){
23173             cfg.cls += ' active';
23174         }
23175         
23176         return cfg;
23177     },
23178     initEvents  : function()
23179     {
23180         //Roo.log('trigger add pane handler');
23181         this.parent().fireEvent('addpane', this)
23182     },
23183     
23184      /**
23185      * Updates the tab title 
23186      * @param {String} html to set the title to.
23187      */
23188     setTitle: function(str)
23189     {
23190         if (!this.tab) {
23191             return;
23192         }
23193         this.title = str;
23194         this.tab.select('a', true).first().dom.innerHTML = str;
23195         
23196     }
23197     
23198     
23199     
23200 });
23201
23202  
23203
23204
23205  /*
23206  * - LGPL
23207  *
23208  * menu
23209  * 
23210  */
23211 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23212
23213 /**
23214  * @class Roo.bootstrap.menu.Menu
23215  * @extends Roo.bootstrap.Component
23216  * Bootstrap Menu class - container for Menu
23217  * @cfg {String} html Text of the menu
23218  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23219  * @cfg {String} icon Font awesome icon
23220  * @cfg {String} pos Menu align to (top | bottom) default bottom
23221  * 
23222  * 
23223  * @constructor
23224  * Create a new Menu
23225  * @param {Object} config The config object
23226  */
23227
23228
23229 Roo.bootstrap.menu.Menu = function(config){
23230     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23231     
23232     this.addEvents({
23233         /**
23234          * @event beforeshow
23235          * Fires before this menu is displayed
23236          * @param {Roo.bootstrap.menu.Menu} this
23237          */
23238         beforeshow : true,
23239         /**
23240          * @event beforehide
23241          * Fires before this menu is hidden
23242          * @param {Roo.bootstrap.menu.Menu} this
23243          */
23244         beforehide : true,
23245         /**
23246          * @event show
23247          * Fires after this menu is displayed
23248          * @param {Roo.bootstrap.menu.Menu} this
23249          */
23250         show : true,
23251         /**
23252          * @event hide
23253          * Fires after this menu is hidden
23254          * @param {Roo.bootstrap.menu.Menu} this
23255          */
23256         hide : true,
23257         /**
23258          * @event click
23259          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23260          * @param {Roo.bootstrap.menu.Menu} this
23261          * @param {Roo.EventObject} e
23262          */
23263         click : true
23264     });
23265     
23266 };
23267
23268 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23269     
23270     submenu : false,
23271     html : '',
23272     weight : 'default',
23273     icon : false,
23274     pos : 'bottom',
23275     
23276     
23277     getChildContainer : function() {
23278         if(this.isSubMenu){
23279             return this.el;
23280         }
23281         
23282         return this.el.select('ul.dropdown-menu', true).first();  
23283     },
23284     
23285     getAutoCreate : function()
23286     {
23287         var text = [
23288             {
23289                 tag : 'span',
23290                 cls : 'roo-menu-text',
23291                 html : this.html
23292             }
23293         ];
23294         
23295         if(this.icon){
23296             text.unshift({
23297                 tag : 'i',
23298                 cls : 'fa ' + this.icon
23299             })
23300         }
23301         
23302         
23303         var cfg = {
23304             tag : 'div',
23305             cls : 'btn-group',
23306             cn : [
23307                 {
23308                     tag : 'button',
23309                     cls : 'dropdown-button btn btn-' + this.weight,
23310                     cn : text
23311                 },
23312                 {
23313                     tag : 'button',
23314                     cls : 'dropdown-toggle btn btn-' + this.weight,
23315                     cn : [
23316                         {
23317                             tag : 'span',
23318                             cls : 'caret'
23319                         }
23320                     ]
23321                 },
23322                 {
23323                     tag : 'ul',
23324                     cls : 'dropdown-menu'
23325                 }
23326             ]
23327             
23328         };
23329         
23330         if(this.pos == 'top'){
23331             cfg.cls += ' dropup';
23332         }
23333         
23334         if(this.isSubMenu){
23335             cfg = {
23336                 tag : 'ul',
23337                 cls : 'dropdown-menu'
23338             }
23339         }
23340         
23341         return cfg;
23342     },
23343     
23344     onRender : function(ct, position)
23345     {
23346         this.isSubMenu = ct.hasClass('dropdown-submenu');
23347         
23348         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23349     },
23350     
23351     initEvents : function() 
23352     {
23353         if(this.isSubMenu){
23354             return;
23355         }
23356         
23357         this.hidden = true;
23358         
23359         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23360         this.triggerEl.on('click', this.onTriggerPress, this);
23361         
23362         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23363         this.buttonEl.on('click', this.onClick, this);
23364         
23365     },
23366     
23367     list : function()
23368     {
23369         if(this.isSubMenu){
23370             return this.el;
23371         }
23372         
23373         return this.el.select('ul.dropdown-menu', true).first();
23374     },
23375     
23376     onClick : function(e)
23377     {
23378         this.fireEvent("click", this, e);
23379     },
23380     
23381     onTriggerPress  : function(e)
23382     {   
23383         if (this.isVisible()) {
23384             this.hide();
23385         } else {
23386             this.show();
23387         }
23388     },
23389     
23390     isVisible : function(){
23391         return !this.hidden;
23392     },
23393     
23394     show : function()
23395     {
23396         this.fireEvent("beforeshow", this);
23397         
23398         this.hidden = false;
23399         this.el.addClass('open');
23400         
23401         Roo.get(document).on("mouseup", this.onMouseUp, this);
23402         
23403         this.fireEvent("show", this);
23404         
23405         
23406     },
23407     
23408     hide : function()
23409     {
23410         this.fireEvent("beforehide", this);
23411         
23412         this.hidden = true;
23413         this.el.removeClass('open');
23414         
23415         Roo.get(document).un("mouseup", this.onMouseUp);
23416         
23417         this.fireEvent("hide", this);
23418     },
23419     
23420     onMouseUp : function()
23421     {
23422         this.hide();
23423     }
23424     
23425 });
23426
23427  
23428  /*
23429  * - LGPL
23430  *
23431  * menu item
23432  * 
23433  */
23434 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23435
23436 /**
23437  * @class Roo.bootstrap.menu.Item
23438  * @extends Roo.bootstrap.Component
23439  * Bootstrap MenuItem class
23440  * @cfg {Boolean} submenu (true | false) default false
23441  * @cfg {String} html text of the item
23442  * @cfg {String} href the link
23443  * @cfg {Boolean} disable (true | false) default false
23444  * @cfg {Boolean} preventDefault (true | false) default true
23445  * @cfg {String} icon Font awesome icon
23446  * @cfg {String} pos Submenu align to (left | right) default right 
23447  * 
23448  * 
23449  * @constructor
23450  * Create a new Item
23451  * @param {Object} config The config object
23452  */
23453
23454
23455 Roo.bootstrap.menu.Item = function(config){
23456     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23457     this.addEvents({
23458         /**
23459          * @event mouseover
23460          * Fires when the mouse is hovering over this menu
23461          * @param {Roo.bootstrap.menu.Item} this
23462          * @param {Roo.EventObject} e
23463          */
23464         mouseover : true,
23465         /**
23466          * @event mouseout
23467          * Fires when the mouse exits this menu
23468          * @param {Roo.bootstrap.menu.Item} this
23469          * @param {Roo.EventObject} e
23470          */
23471         mouseout : true,
23472         // raw events
23473         /**
23474          * @event click
23475          * The raw click event for the entire grid.
23476          * @param {Roo.EventObject} e
23477          */
23478         click : true
23479     });
23480 };
23481
23482 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23483     
23484     submenu : false,
23485     href : '',
23486     html : '',
23487     preventDefault: true,
23488     disable : false,
23489     icon : false,
23490     pos : 'right',
23491     
23492     getAutoCreate : function()
23493     {
23494         var text = [
23495             {
23496                 tag : 'span',
23497                 cls : 'roo-menu-item-text',
23498                 html : this.html
23499             }
23500         ];
23501         
23502         if(this.icon){
23503             text.unshift({
23504                 tag : 'i',
23505                 cls : 'fa ' + this.icon
23506             })
23507         }
23508         
23509         var cfg = {
23510             tag : 'li',
23511             cn : [
23512                 {
23513                     tag : 'a',
23514                     href : this.href || '#',
23515                     cn : text
23516                 }
23517             ]
23518         };
23519         
23520         if(this.disable){
23521             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23522         }
23523         
23524         if(this.submenu){
23525             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23526             
23527             if(this.pos == 'left'){
23528                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23529             }
23530         }
23531         
23532         return cfg;
23533     },
23534     
23535     initEvents : function() 
23536     {
23537         this.el.on('mouseover', this.onMouseOver, this);
23538         this.el.on('mouseout', this.onMouseOut, this);
23539         
23540         this.el.select('a', true).first().on('click', this.onClick, this);
23541         
23542     },
23543     
23544     onClick : function(e)
23545     {
23546         if(this.preventDefault){
23547             e.preventDefault();
23548         }
23549         
23550         this.fireEvent("click", this, e);
23551     },
23552     
23553     onMouseOver : function(e)
23554     {
23555         if(this.submenu && this.pos == 'left'){
23556             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23557         }
23558         
23559         this.fireEvent("mouseover", this, e);
23560     },
23561     
23562     onMouseOut : function(e)
23563     {
23564         this.fireEvent("mouseout", this, e);
23565     }
23566 });
23567
23568  
23569
23570  /*
23571  * - LGPL
23572  *
23573  * menu separator
23574  * 
23575  */
23576 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23577
23578 /**
23579  * @class Roo.bootstrap.menu.Separator
23580  * @extends Roo.bootstrap.Component
23581  * Bootstrap Separator class
23582  * 
23583  * @constructor
23584  * Create a new Separator
23585  * @param {Object} config The config object
23586  */
23587
23588
23589 Roo.bootstrap.menu.Separator = function(config){
23590     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23591 };
23592
23593 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23594     
23595     getAutoCreate : function(){
23596         var cfg = {
23597             tag : 'li',
23598             cls: 'divider'
23599         };
23600         
23601         return cfg;
23602     }
23603    
23604 });
23605
23606  
23607
23608  /*
23609  * - LGPL
23610  *
23611  * Tooltip
23612  * 
23613  */
23614
23615 /**
23616  * @class Roo.bootstrap.Tooltip
23617  * Bootstrap Tooltip class
23618  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23619  * to determine which dom element triggers the tooltip.
23620  * 
23621  * It needs to add support for additional attributes like tooltip-position
23622  * 
23623  * @constructor
23624  * Create a new Toolti
23625  * @param {Object} config The config object
23626  */
23627
23628 Roo.bootstrap.Tooltip = function(config){
23629     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23630 };
23631
23632 Roo.apply(Roo.bootstrap.Tooltip, {
23633     /**
23634      * @function init initialize tooltip monitoring.
23635      * @static
23636      */
23637     currentEl : false,
23638     currentTip : false,
23639     currentRegion : false,
23640     
23641     //  init : delay?
23642     
23643     init : function()
23644     {
23645         Roo.get(document).on('mouseover', this.enter ,this);
23646         Roo.get(document).on('mouseout', this.leave, this);
23647          
23648         
23649         this.currentTip = new Roo.bootstrap.Tooltip();
23650     },
23651     
23652     enter : function(ev)
23653     {
23654         var dom = ev.getTarget();
23655         
23656         //Roo.log(['enter',dom]);
23657         var el = Roo.fly(dom);
23658         if (this.currentEl) {
23659             //Roo.log(dom);
23660             //Roo.log(this.currentEl);
23661             //Roo.log(this.currentEl.contains(dom));
23662             if (this.currentEl == el) {
23663                 return;
23664             }
23665             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23666                 return;
23667             }
23668
23669         }
23670         
23671         if (this.currentTip.el) {
23672             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23673         }    
23674         //Roo.log(ev);
23675         var bindEl = el;
23676         
23677         // you can not look for children, as if el is the body.. then everythign is the child..
23678         if (!el.attr('tooltip')) { //
23679             if (!el.select("[tooltip]").elements.length) {
23680                 return;
23681             }
23682             // is the mouse over this child...?
23683             bindEl = el.select("[tooltip]").first();
23684             var xy = ev.getXY();
23685             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23686                 //Roo.log("not in region.");
23687                 return;
23688             }
23689             //Roo.log("child element over..");
23690             
23691         }
23692         this.currentEl = bindEl;
23693         this.currentTip.bind(bindEl);
23694         this.currentRegion = Roo.lib.Region.getRegion(dom);
23695         this.currentTip.enter();
23696         
23697     },
23698     leave : function(ev)
23699     {
23700         var dom = ev.getTarget();
23701         //Roo.log(['leave',dom]);
23702         if (!this.currentEl) {
23703             return;
23704         }
23705         
23706         
23707         if (dom != this.currentEl.dom) {
23708             return;
23709         }
23710         var xy = ev.getXY();
23711         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23712             return;
23713         }
23714         // only activate leave if mouse cursor is outside... bounding box..
23715         
23716         
23717         
23718         
23719         if (this.currentTip) {
23720             this.currentTip.leave();
23721         }
23722         //Roo.log('clear currentEl');
23723         this.currentEl = false;
23724         
23725         
23726     },
23727     alignment : {
23728         'left' : ['r-l', [-2,0], 'right'],
23729         'right' : ['l-r', [2,0], 'left'],
23730         'bottom' : ['t-b', [0,2], 'top'],
23731         'top' : [ 'b-t', [0,-2], 'bottom']
23732     }
23733     
23734 });
23735
23736
23737 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23738     
23739     
23740     bindEl : false,
23741     
23742     delay : null, // can be { show : 300 , hide: 500}
23743     
23744     timeout : null,
23745     
23746     hoverState : null, //???
23747     
23748     placement : 'bottom', 
23749     
23750     getAutoCreate : function(){
23751     
23752         var cfg = {
23753            cls : 'tooltip',
23754            role : 'tooltip',
23755            cn : [
23756                 {
23757                     cls : 'tooltip-arrow'
23758                 },
23759                 {
23760                     cls : 'tooltip-inner'
23761                 }
23762            ]
23763         };
23764         
23765         return cfg;
23766     },
23767     bind : function(el)
23768     {
23769         this.bindEl = el;
23770     },
23771       
23772     
23773     enter : function () {
23774        
23775         if (this.timeout != null) {
23776             clearTimeout(this.timeout);
23777         }
23778         
23779         this.hoverState = 'in';
23780          //Roo.log("enter - show");
23781         if (!this.delay || !this.delay.show) {
23782             this.show();
23783             return;
23784         }
23785         var _t = this;
23786         this.timeout = setTimeout(function () {
23787             if (_t.hoverState == 'in') {
23788                 _t.show();
23789             }
23790         }, this.delay.show);
23791     },
23792     leave : function()
23793     {
23794         clearTimeout(this.timeout);
23795     
23796         this.hoverState = 'out';
23797          if (!this.delay || !this.delay.hide) {
23798             this.hide();
23799             return;
23800         }
23801        
23802         var _t = this;
23803         this.timeout = setTimeout(function () {
23804             //Roo.log("leave - timeout");
23805             
23806             if (_t.hoverState == 'out') {
23807                 _t.hide();
23808                 Roo.bootstrap.Tooltip.currentEl = false;
23809             }
23810         }, delay);
23811     },
23812     
23813     show : function ()
23814     {
23815         if (!this.el) {
23816             this.render(document.body);
23817         }
23818         // set content.
23819         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23820         
23821         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23822         
23823         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23824         
23825         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23826         
23827         var placement = typeof this.placement == 'function' ?
23828             this.placement.call(this, this.el, on_el) :
23829             this.placement;
23830             
23831         var autoToken = /\s?auto?\s?/i;
23832         var autoPlace = autoToken.test(placement);
23833         if (autoPlace) {
23834             placement = placement.replace(autoToken, '') || 'top';
23835         }
23836         
23837         //this.el.detach()
23838         //this.el.setXY([0,0]);
23839         this.el.show();
23840         //this.el.dom.style.display='block';
23841         
23842         //this.el.appendTo(on_el);
23843         
23844         var p = this.getPosition();
23845         var box = this.el.getBox();
23846         
23847         if (autoPlace) {
23848             // fixme..
23849         }
23850         
23851         var align = Roo.bootstrap.Tooltip.alignment[placement];
23852         
23853         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23854         
23855         if(placement == 'top' || placement == 'bottom'){
23856             if(xy[0] < 0){
23857                 placement = 'right';
23858             }
23859             
23860             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23861                 placement = 'left';
23862             }
23863         }
23864         
23865         align = Roo.bootstrap.Tooltip.alignment[placement];
23866         
23867         this.el.alignTo(this.bindEl, align[0],align[1]);
23868         //var arrow = this.el.select('.arrow',true).first();
23869         //arrow.set(align[2], 
23870         
23871         this.el.addClass(placement);
23872         
23873         this.el.addClass('in fade');
23874         
23875         this.hoverState = null;
23876         
23877         if (this.el.hasClass('fade')) {
23878             // fade it?
23879         }
23880         
23881     },
23882     hide : function()
23883     {
23884          
23885         if (!this.el) {
23886             return;
23887         }
23888         //this.el.setXY([0,0]);
23889         this.el.removeClass('in');
23890         //this.el.hide();
23891         
23892     }
23893     
23894 });
23895  
23896
23897  /*
23898  * - LGPL
23899  *
23900  * Location Picker
23901  * 
23902  */
23903
23904 /**
23905  * @class Roo.bootstrap.LocationPicker
23906  * @extends Roo.bootstrap.Component
23907  * Bootstrap LocationPicker class
23908  * @cfg {Number} latitude Position when init default 0
23909  * @cfg {Number} longitude Position when init default 0
23910  * @cfg {Number} zoom default 15
23911  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23912  * @cfg {Boolean} mapTypeControl default false
23913  * @cfg {Boolean} disableDoubleClickZoom default false
23914  * @cfg {Boolean} scrollwheel default true
23915  * @cfg {Boolean} streetViewControl default false
23916  * @cfg {Number} radius default 0
23917  * @cfg {String} locationName
23918  * @cfg {Boolean} draggable default true
23919  * @cfg {Boolean} enableAutocomplete default false
23920  * @cfg {Boolean} enableReverseGeocode default true
23921  * @cfg {String} markerTitle
23922  * 
23923  * @constructor
23924  * Create a new LocationPicker
23925  * @param {Object} config The config object
23926  */
23927
23928
23929 Roo.bootstrap.LocationPicker = function(config){
23930     
23931     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23932     
23933     this.addEvents({
23934         /**
23935          * @event initial
23936          * Fires when the picker initialized.
23937          * @param {Roo.bootstrap.LocationPicker} this
23938          * @param {Google Location} location
23939          */
23940         initial : true,
23941         /**
23942          * @event positionchanged
23943          * Fires when the picker position changed.
23944          * @param {Roo.bootstrap.LocationPicker} this
23945          * @param {Google Location} location
23946          */
23947         positionchanged : true,
23948         /**
23949          * @event resize
23950          * Fires when the map resize.
23951          * @param {Roo.bootstrap.LocationPicker} this
23952          */
23953         resize : true,
23954         /**
23955          * @event show
23956          * Fires when the map show.
23957          * @param {Roo.bootstrap.LocationPicker} this
23958          */
23959         show : true,
23960         /**
23961          * @event hide
23962          * Fires when the map hide.
23963          * @param {Roo.bootstrap.LocationPicker} this
23964          */
23965         hide : true,
23966         /**
23967          * @event mapClick
23968          * Fires when click the map.
23969          * @param {Roo.bootstrap.LocationPicker} this
23970          * @param {Map event} e
23971          */
23972         mapClick : true,
23973         /**
23974          * @event mapRightClick
23975          * Fires when right click the map.
23976          * @param {Roo.bootstrap.LocationPicker} this
23977          * @param {Map event} e
23978          */
23979         mapRightClick : true,
23980         /**
23981          * @event markerClick
23982          * Fires when click the marker.
23983          * @param {Roo.bootstrap.LocationPicker} this
23984          * @param {Map event} e
23985          */
23986         markerClick : true,
23987         /**
23988          * @event markerRightClick
23989          * Fires when right click the marker.
23990          * @param {Roo.bootstrap.LocationPicker} this
23991          * @param {Map event} e
23992          */
23993         markerRightClick : true,
23994         /**
23995          * @event OverlayViewDraw
23996          * Fires when OverlayView Draw
23997          * @param {Roo.bootstrap.LocationPicker} this
23998          */
23999         OverlayViewDraw : true,
24000         /**
24001          * @event OverlayViewOnAdd
24002          * Fires when OverlayView Draw
24003          * @param {Roo.bootstrap.LocationPicker} this
24004          */
24005         OverlayViewOnAdd : true,
24006         /**
24007          * @event OverlayViewOnRemove
24008          * Fires when OverlayView Draw
24009          * @param {Roo.bootstrap.LocationPicker} this
24010          */
24011         OverlayViewOnRemove : true,
24012         /**
24013          * @event OverlayViewShow
24014          * Fires when OverlayView Draw
24015          * @param {Roo.bootstrap.LocationPicker} this
24016          * @param {Pixel} cpx
24017          */
24018         OverlayViewShow : true,
24019         /**
24020          * @event OverlayViewHide
24021          * Fires when OverlayView Draw
24022          * @param {Roo.bootstrap.LocationPicker} this
24023          */
24024         OverlayViewHide : true,
24025         /**
24026          * @event loadexception
24027          * Fires when load google lib failed.
24028          * @param {Roo.bootstrap.LocationPicker} this
24029          */
24030         loadexception : true
24031     });
24032         
24033 };
24034
24035 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24036     
24037     gMapContext: false,
24038     
24039     latitude: 0,
24040     longitude: 0,
24041     zoom: 15,
24042     mapTypeId: false,
24043     mapTypeControl: false,
24044     disableDoubleClickZoom: false,
24045     scrollwheel: true,
24046     streetViewControl: false,
24047     radius: 0,
24048     locationName: '',
24049     draggable: true,
24050     enableAutocomplete: false,
24051     enableReverseGeocode: true,
24052     markerTitle: '',
24053     
24054     getAutoCreate: function()
24055     {
24056
24057         var cfg = {
24058             tag: 'div',
24059             cls: 'roo-location-picker'
24060         };
24061         
24062         return cfg
24063     },
24064     
24065     initEvents: function(ct, position)
24066     {       
24067         if(!this.el.getWidth() || this.isApplied()){
24068             return;
24069         }
24070         
24071         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24072         
24073         this.initial();
24074     },
24075     
24076     initial: function()
24077     {
24078         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24079             this.fireEvent('loadexception', this);
24080             return;
24081         }
24082         
24083         if(!this.mapTypeId){
24084             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24085         }
24086         
24087         this.gMapContext = this.GMapContext();
24088         
24089         this.initOverlayView();
24090         
24091         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24092         
24093         var _this = this;
24094                 
24095         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24096             _this.setPosition(_this.gMapContext.marker.position);
24097         });
24098         
24099         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24100             _this.fireEvent('mapClick', this, event);
24101             
24102         });
24103
24104         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24105             _this.fireEvent('mapRightClick', this, event);
24106             
24107         });
24108         
24109         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24110             _this.fireEvent('markerClick', this, event);
24111             
24112         });
24113
24114         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24115             _this.fireEvent('markerRightClick', this, event);
24116             
24117         });
24118         
24119         this.setPosition(this.gMapContext.location);
24120         
24121         this.fireEvent('initial', this, this.gMapContext.location);
24122     },
24123     
24124     initOverlayView: function()
24125     {
24126         var _this = this;
24127         
24128         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24129             
24130             draw: function()
24131             {
24132                 _this.fireEvent('OverlayViewDraw', _this);
24133             },
24134             
24135             onAdd: function()
24136             {
24137                 _this.fireEvent('OverlayViewOnAdd', _this);
24138             },
24139             
24140             onRemove: function()
24141             {
24142                 _this.fireEvent('OverlayViewOnRemove', _this);
24143             },
24144             
24145             show: function(cpx)
24146             {
24147                 _this.fireEvent('OverlayViewShow', _this, cpx);
24148             },
24149             
24150             hide: function()
24151             {
24152                 _this.fireEvent('OverlayViewHide', _this);
24153             }
24154             
24155         });
24156     },
24157     
24158     fromLatLngToContainerPixel: function(event)
24159     {
24160         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24161     },
24162     
24163     isApplied: function() 
24164     {
24165         return this.getGmapContext() == false ? false : true;
24166     },
24167     
24168     getGmapContext: function() 
24169     {
24170         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24171     },
24172     
24173     GMapContext: function() 
24174     {
24175         var position = new google.maps.LatLng(this.latitude, this.longitude);
24176         
24177         var _map = new google.maps.Map(this.el.dom, {
24178             center: position,
24179             zoom: this.zoom,
24180             mapTypeId: this.mapTypeId,
24181             mapTypeControl: this.mapTypeControl,
24182             disableDoubleClickZoom: this.disableDoubleClickZoom,
24183             scrollwheel: this.scrollwheel,
24184             streetViewControl: this.streetViewControl,
24185             locationName: this.locationName,
24186             draggable: this.draggable,
24187             enableAutocomplete: this.enableAutocomplete,
24188             enableReverseGeocode: this.enableReverseGeocode
24189         });
24190         
24191         var _marker = new google.maps.Marker({
24192             position: position,
24193             map: _map,
24194             title: this.markerTitle,
24195             draggable: this.draggable
24196         });
24197         
24198         return {
24199             map: _map,
24200             marker: _marker,
24201             circle: null,
24202             location: position,
24203             radius: this.radius,
24204             locationName: this.locationName,
24205             addressComponents: {
24206                 formatted_address: null,
24207                 addressLine1: null,
24208                 addressLine2: null,
24209                 streetName: null,
24210                 streetNumber: null,
24211                 city: null,
24212                 district: null,
24213                 state: null,
24214                 stateOrProvince: null
24215             },
24216             settings: this,
24217             domContainer: this.el.dom,
24218             geodecoder: new google.maps.Geocoder()
24219         };
24220     },
24221     
24222     drawCircle: function(center, radius, options) 
24223     {
24224         if (this.gMapContext.circle != null) {
24225             this.gMapContext.circle.setMap(null);
24226         }
24227         if (radius > 0) {
24228             radius *= 1;
24229             options = Roo.apply({}, options, {
24230                 strokeColor: "#0000FF",
24231                 strokeOpacity: .35,
24232                 strokeWeight: 2,
24233                 fillColor: "#0000FF",
24234                 fillOpacity: .2
24235             });
24236             
24237             options.map = this.gMapContext.map;
24238             options.radius = radius;
24239             options.center = center;
24240             this.gMapContext.circle = new google.maps.Circle(options);
24241             return this.gMapContext.circle;
24242         }
24243         
24244         return null;
24245     },
24246     
24247     setPosition: function(location) 
24248     {
24249         this.gMapContext.location = location;
24250         this.gMapContext.marker.setPosition(location);
24251         this.gMapContext.map.panTo(location);
24252         this.drawCircle(location, this.gMapContext.radius, {});
24253         
24254         var _this = this;
24255         
24256         if (this.gMapContext.settings.enableReverseGeocode) {
24257             this.gMapContext.geodecoder.geocode({
24258                 latLng: this.gMapContext.location
24259             }, function(results, status) {
24260                 
24261                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24262                     _this.gMapContext.locationName = results[0].formatted_address;
24263                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24264                     
24265                     _this.fireEvent('positionchanged', this, location);
24266                 }
24267             });
24268             
24269             return;
24270         }
24271         
24272         this.fireEvent('positionchanged', this, location);
24273     },
24274     
24275     resize: function()
24276     {
24277         google.maps.event.trigger(this.gMapContext.map, "resize");
24278         
24279         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24280         
24281         this.fireEvent('resize', this);
24282     },
24283     
24284     setPositionByLatLng: function(latitude, longitude)
24285     {
24286         this.setPosition(new google.maps.LatLng(latitude, longitude));
24287     },
24288     
24289     getCurrentPosition: function() 
24290     {
24291         return {
24292             latitude: this.gMapContext.location.lat(),
24293             longitude: this.gMapContext.location.lng()
24294         };
24295     },
24296     
24297     getAddressName: function() 
24298     {
24299         return this.gMapContext.locationName;
24300     },
24301     
24302     getAddressComponents: function() 
24303     {
24304         return this.gMapContext.addressComponents;
24305     },
24306     
24307     address_component_from_google_geocode: function(address_components) 
24308     {
24309         var result = {};
24310         
24311         for (var i = 0; i < address_components.length; i++) {
24312             var component = address_components[i];
24313             if (component.types.indexOf("postal_code") >= 0) {
24314                 result.postalCode = component.short_name;
24315             } else if (component.types.indexOf("street_number") >= 0) {
24316                 result.streetNumber = component.short_name;
24317             } else if (component.types.indexOf("route") >= 0) {
24318                 result.streetName = component.short_name;
24319             } else if (component.types.indexOf("neighborhood") >= 0) {
24320                 result.city = component.short_name;
24321             } else if (component.types.indexOf("locality") >= 0) {
24322                 result.city = component.short_name;
24323             } else if (component.types.indexOf("sublocality") >= 0) {
24324                 result.district = component.short_name;
24325             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24326                 result.stateOrProvince = component.short_name;
24327             } else if (component.types.indexOf("country") >= 0) {
24328                 result.country = component.short_name;
24329             }
24330         }
24331         
24332         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24333         result.addressLine2 = "";
24334         return result;
24335     },
24336     
24337     setZoomLevel: function(zoom)
24338     {
24339         this.gMapContext.map.setZoom(zoom);
24340     },
24341     
24342     show: function()
24343     {
24344         if(!this.el){
24345             return;
24346         }
24347         
24348         this.el.show();
24349         
24350         this.resize();
24351         
24352         this.fireEvent('show', this);
24353     },
24354     
24355     hide: function()
24356     {
24357         if(!this.el){
24358             return;
24359         }
24360         
24361         this.el.hide();
24362         
24363         this.fireEvent('hide', this);
24364     }
24365     
24366 });
24367
24368 Roo.apply(Roo.bootstrap.LocationPicker, {
24369     
24370     OverlayView : function(map, options)
24371     {
24372         options = options || {};
24373         
24374         this.setMap(map);
24375     }
24376     
24377     
24378 });/*
24379  * - LGPL
24380  *
24381  * Alert
24382  * 
24383  */
24384
24385 /**
24386  * @class Roo.bootstrap.Alert
24387  * @extends Roo.bootstrap.Component
24388  * Bootstrap Alert class
24389  * @cfg {String} title The title of alert
24390  * @cfg {String} html The content of alert
24391  * @cfg {String} weight (  success | info | warning | danger )
24392  * @cfg {String} faicon font-awesomeicon
24393  * 
24394  * @constructor
24395  * Create a new alert
24396  * @param {Object} config The config object
24397  */
24398
24399
24400 Roo.bootstrap.Alert = function(config){
24401     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24402     
24403 };
24404
24405 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24406     
24407     title: '',
24408     html: '',
24409     weight: false,
24410     faicon: false,
24411     
24412     getAutoCreate : function()
24413     {
24414         
24415         var cfg = {
24416             tag : 'div',
24417             cls : 'alert',
24418             cn : [
24419                 {
24420                     tag : 'i',
24421                     cls : 'roo-alert-icon'
24422                     
24423                 },
24424                 {
24425                     tag : 'b',
24426                     cls : 'roo-alert-title',
24427                     html : this.title
24428                 },
24429                 {
24430                     tag : 'span',
24431                     cls : 'roo-alert-text',
24432                     html : this.html
24433                 }
24434             ]
24435         };
24436         
24437         if(this.faicon){
24438             cfg.cn[0].cls += ' fa ' + this.faicon;
24439         }
24440         
24441         if(this.weight){
24442             cfg.cls += ' alert-' + this.weight;
24443         }
24444         
24445         return cfg;
24446     },
24447     
24448     initEvents: function() 
24449     {
24450         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24451     },
24452     
24453     setTitle : function(str)
24454     {
24455         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24456     },
24457     
24458     setText : function(str)
24459     {
24460         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24461     },
24462     
24463     setWeight : function(weight)
24464     {
24465         if(this.weight){
24466             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24467         }
24468         
24469         this.weight = weight;
24470         
24471         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24472     },
24473     
24474     setIcon : function(icon)
24475     {
24476         if(this.faicon){
24477             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24478         }
24479         
24480         this.faicon = icon;
24481         
24482         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24483     },
24484     
24485     hide: function() 
24486     {
24487         this.el.hide();   
24488     },
24489     
24490     show: function() 
24491     {  
24492         this.el.show();   
24493     }
24494     
24495 });
24496
24497  
24498 /*
24499 * Licence: LGPL
24500 */
24501
24502 /**
24503  * @class Roo.bootstrap.UploadCropbox
24504  * @extends Roo.bootstrap.Component
24505  * Bootstrap UploadCropbox class
24506  * @cfg {String} emptyText show when image has been loaded
24507  * @cfg {String} rotateNotify show when image too small to rotate
24508  * @cfg {Number} errorTimeout default 3000
24509  * @cfg {Number} minWidth default 300
24510  * @cfg {Number} minHeight default 300
24511  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24512  * @cfg {Boolean} isDocument (true|false) default false
24513  * @cfg {String} url action url
24514  * @cfg {String} paramName default 'imageUpload'
24515  * @cfg {String} method default POST
24516  * @cfg {Boolean} loadMask (true|false) default true
24517  * @cfg {Boolean} loadingText default 'Loading...'
24518  * 
24519  * @constructor
24520  * Create a new UploadCropbox
24521  * @param {Object} config The config object
24522  */
24523
24524 Roo.bootstrap.UploadCropbox = function(config){
24525     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24526     
24527     this.addEvents({
24528         /**
24529          * @event beforeselectfile
24530          * Fire before select file
24531          * @param {Roo.bootstrap.UploadCropbox} this
24532          */
24533         "beforeselectfile" : true,
24534         /**
24535          * @event initial
24536          * Fire after initEvent
24537          * @param {Roo.bootstrap.UploadCropbox} this
24538          */
24539         "initial" : true,
24540         /**
24541          * @event crop
24542          * Fire after initEvent
24543          * @param {Roo.bootstrap.UploadCropbox} this
24544          * @param {String} data
24545          */
24546         "crop" : true,
24547         /**
24548          * @event prepare
24549          * Fire when preparing the file data
24550          * @param {Roo.bootstrap.UploadCropbox} this
24551          * @param {Object} file
24552          */
24553         "prepare" : true,
24554         /**
24555          * @event exception
24556          * Fire when get exception
24557          * @param {Roo.bootstrap.UploadCropbox} this
24558          * @param {XMLHttpRequest} xhr
24559          */
24560         "exception" : true,
24561         /**
24562          * @event beforeloadcanvas
24563          * Fire before load the canvas
24564          * @param {Roo.bootstrap.UploadCropbox} this
24565          * @param {String} src
24566          */
24567         "beforeloadcanvas" : true,
24568         /**
24569          * @event trash
24570          * Fire when trash image
24571          * @param {Roo.bootstrap.UploadCropbox} this
24572          */
24573         "trash" : true,
24574         /**
24575          * @event download
24576          * Fire when download the image
24577          * @param {Roo.bootstrap.UploadCropbox} this
24578          */
24579         "download" : true,
24580         /**
24581          * @event footerbuttonclick
24582          * Fire when footerbuttonclick
24583          * @param {Roo.bootstrap.UploadCropbox} this
24584          * @param {String} type
24585          */
24586         "footerbuttonclick" : true,
24587         /**
24588          * @event resize
24589          * Fire when resize
24590          * @param {Roo.bootstrap.UploadCropbox} this
24591          */
24592         "resize" : true,
24593         /**
24594          * @event rotate
24595          * Fire when rotate the image
24596          * @param {Roo.bootstrap.UploadCropbox} this
24597          * @param {String} pos
24598          */
24599         "rotate" : true,
24600         /**
24601          * @event inspect
24602          * Fire when inspect the file
24603          * @param {Roo.bootstrap.UploadCropbox} this
24604          * @param {Object} file
24605          */
24606         "inspect" : true,
24607         /**
24608          * @event upload
24609          * Fire when xhr upload the file
24610          * @param {Roo.bootstrap.UploadCropbox} this
24611          * @param {Object} data
24612          */
24613         "upload" : true,
24614         /**
24615          * @event arrange
24616          * Fire when arrange the file data
24617          * @param {Roo.bootstrap.UploadCropbox} this
24618          * @param {Object} formData
24619          */
24620         "arrange" : true
24621     });
24622     
24623     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24624 };
24625
24626 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24627     
24628     emptyText : 'Click to upload image',
24629     rotateNotify : 'Image is too small to rotate',
24630     errorTimeout : 3000,
24631     scale : 0,
24632     baseScale : 1,
24633     rotate : 0,
24634     dragable : false,
24635     pinching : false,
24636     mouseX : 0,
24637     mouseY : 0,
24638     cropData : false,
24639     minWidth : 300,
24640     minHeight : 300,
24641     file : false,
24642     exif : {},
24643     baseRotate : 1,
24644     cropType : 'image/jpeg',
24645     buttons : false,
24646     canvasLoaded : false,
24647     isDocument : false,
24648     method : 'POST',
24649     paramName : 'imageUpload',
24650     loadMask : true,
24651     loadingText : 'Loading...',
24652     maskEl : false,
24653     
24654     getAutoCreate : function()
24655     {
24656         var cfg = {
24657             tag : 'div',
24658             cls : 'roo-upload-cropbox',
24659             cn : [
24660                 {
24661                     tag : 'input',
24662                     cls : 'roo-upload-cropbox-selector',
24663                     type : 'file'
24664                 },
24665                 {
24666                     tag : 'div',
24667                     cls : 'roo-upload-cropbox-body',
24668                     style : 'cursor:pointer',
24669                     cn : [
24670                         {
24671                             tag : 'div',
24672                             cls : 'roo-upload-cropbox-preview'
24673                         },
24674                         {
24675                             tag : 'div',
24676                             cls : 'roo-upload-cropbox-thumb'
24677                         },
24678                         {
24679                             tag : 'div',
24680                             cls : 'roo-upload-cropbox-empty-notify',
24681                             html : this.emptyText
24682                         },
24683                         {
24684                             tag : 'div',
24685                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24686                             html : this.rotateNotify
24687                         }
24688                     ]
24689                 },
24690                 {
24691                     tag : 'div',
24692                     cls : 'roo-upload-cropbox-footer',
24693                     cn : {
24694                         tag : 'div',
24695                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24696                         cn : []
24697                     }
24698                 }
24699             ]
24700         };
24701         
24702         return cfg;
24703     },
24704     
24705     onRender : function(ct, position)
24706     {
24707         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24708         
24709         if (this.buttons.length) {
24710             
24711             Roo.each(this.buttons, function(bb) {
24712                 
24713                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24714                 
24715                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24716                 
24717             }, this);
24718         }
24719         
24720         if(this.loadMask){
24721             this.maskEl = this.el;
24722         }
24723     },
24724     
24725     initEvents : function()
24726     {
24727         this.urlAPI = (window.createObjectURL && window) || 
24728                                 (window.URL && URL.revokeObjectURL && URL) || 
24729                                 (window.webkitURL && webkitURL);
24730                         
24731         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24732         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24733         
24734         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24735         this.selectorEl.hide();
24736         
24737         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24738         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24739         
24740         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24741         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24742         this.thumbEl.hide();
24743         
24744         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24745         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24746         
24747         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24748         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24749         this.errorEl.hide();
24750         
24751         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24752         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24753         this.footerEl.hide();
24754         
24755         this.setThumbBoxSize();
24756         
24757         this.bind();
24758         
24759         this.resize();
24760         
24761         this.fireEvent('initial', this);
24762     },
24763
24764     bind : function()
24765     {
24766         var _this = this;
24767         
24768         window.addEventListener("resize", function() { _this.resize(); } );
24769         
24770         this.bodyEl.on('click', this.beforeSelectFile, this);
24771         
24772         if(Roo.isTouch){
24773             this.bodyEl.on('touchstart', this.onTouchStart, this);
24774             this.bodyEl.on('touchmove', this.onTouchMove, this);
24775             this.bodyEl.on('touchend', this.onTouchEnd, this);
24776         }
24777         
24778         if(!Roo.isTouch){
24779             this.bodyEl.on('mousedown', this.onMouseDown, this);
24780             this.bodyEl.on('mousemove', this.onMouseMove, this);
24781             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24782             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24783             Roo.get(document).on('mouseup', this.onMouseUp, this);
24784         }
24785         
24786         this.selectorEl.on('change', this.onFileSelected, this);
24787     },
24788     
24789     reset : function()
24790     {    
24791         this.scale = 0;
24792         this.baseScale = 1;
24793         this.rotate = 0;
24794         this.baseRotate = 1;
24795         this.dragable = false;
24796         this.pinching = false;
24797         this.mouseX = 0;
24798         this.mouseY = 0;
24799         this.cropData = false;
24800         this.notifyEl.dom.innerHTML = this.emptyText;
24801         
24802         this.selectorEl.dom.value = '';
24803         
24804     },
24805     
24806     resize : function()
24807     {
24808         if(this.fireEvent('resize', this) != false){
24809             this.setThumbBoxPosition();
24810             this.setCanvasPosition();
24811         }
24812     },
24813     
24814     onFooterButtonClick : function(e, el, o, type)
24815     {
24816         switch (type) {
24817             case 'rotate-left' :
24818                 this.onRotateLeft(e);
24819                 break;
24820             case 'rotate-right' :
24821                 this.onRotateRight(e);
24822                 break;
24823             case 'picture' :
24824                 this.beforeSelectFile(e);
24825                 break;
24826             case 'trash' :
24827                 this.trash(e);
24828                 break;
24829             case 'crop' :
24830                 this.crop(e);
24831                 break;
24832             case 'download' :
24833                 this.download(e);
24834                 break;
24835             default :
24836                 break;
24837         }
24838         
24839         this.fireEvent('footerbuttonclick', this, type);
24840     },
24841     
24842     beforeSelectFile : function(e)
24843     {
24844         e.preventDefault();
24845         
24846         if(this.fireEvent('beforeselectfile', this) != false){
24847             this.selectorEl.dom.click();
24848         }
24849     },
24850     
24851     onFileSelected : function(e)
24852     {
24853         e.preventDefault();
24854         
24855         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24856             return;
24857         }
24858         
24859         var file = this.selectorEl.dom.files[0];
24860         
24861         if(this.fireEvent('inspect', this, file) != false){
24862             this.prepare(file);
24863         }
24864         
24865     },
24866     
24867     trash : function(e)
24868     {
24869         this.fireEvent('trash', this);
24870     },
24871     
24872     download : function(e)
24873     {
24874         this.fireEvent('download', this);
24875     },
24876     
24877     loadCanvas : function(src)
24878     {   
24879         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24880             
24881             this.reset();
24882             
24883             this.imageEl = document.createElement('img');
24884             
24885             var _this = this;
24886             
24887             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24888             
24889             this.imageEl.src = src;
24890         }
24891     },
24892     
24893     onLoadCanvas : function()
24894     {   
24895         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24896         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24897         
24898         this.bodyEl.un('click', this.beforeSelectFile, this);
24899         
24900         this.notifyEl.hide();
24901         this.thumbEl.show();
24902         this.footerEl.show();
24903         
24904         this.baseRotateLevel();
24905         
24906         if(this.isDocument){
24907             this.setThumbBoxSize();
24908         }
24909         
24910         this.setThumbBoxPosition();
24911         
24912         this.baseScaleLevel();
24913         
24914         this.draw();
24915         
24916         this.resize();
24917         
24918         this.canvasLoaded = true;
24919         
24920         if(this.loadMask){
24921             this.maskEl.unmask();
24922         }
24923         
24924     },
24925     
24926     setCanvasPosition : function()
24927     {   
24928         if(!this.canvasEl){
24929             return;
24930         }
24931         
24932         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24933         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24934         
24935         this.previewEl.setLeft(pw);
24936         this.previewEl.setTop(ph);
24937         
24938     },
24939     
24940     onMouseDown : function(e)
24941     {   
24942         e.stopEvent();
24943         
24944         this.dragable = true;
24945         this.pinching = false;
24946         
24947         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24948             this.dragable = false;
24949             return;
24950         }
24951         
24952         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24953         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24954         
24955     },
24956     
24957     onMouseMove : function(e)
24958     {   
24959         e.stopEvent();
24960         
24961         if(!this.canvasLoaded){
24962             return;
24963         }
24964         
24965         if (!this.dragable){
24966             return;
24967         }
24968         
24969         var minX = Math.ceil(this.thumbEl.getLeft(true));
24970         var minY = Math.ceil(this.thumbEl.getTop(true));
24971         
24972         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24973         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24974         
24975         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24976         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24977         
24978         x = x - this.mouseX;
24979         y = y - this.mouseY;
24980         
24981         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24982         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24983         
24984         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24985         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24986         
24987         this.previewEl.setLeft(bgX);
24988         this.previewEl.setTop(bgY);
24989         
24990         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24991         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24992     },
24993     
24994     onMouseUp : function(e)
24995     {   
24996         e.stopEvent();
24997         
24998         this.dragable = false;
24999     },
25000     
25001     onMouseWheel : function(e)
25002     {   
25003         e.stopEvent();
25004         
25005         this.startScale = this.scale;
25006         
25007         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25008         
25009         if(!this.zoomable()){
25010             this.scale = this.startScale;
25011             return;
25012         }
25013         
25014         this.draw();
25015         
25016         return;
25017     },
25018     
25019     zoomable : function()
25020     {
25021         var minScale = this.thumbEl.getWidth() / this.minWidth;
25022         
25023         if(this.minWidth < this.minHeight){
25024             minScale = this.thumbEl.getHeight() / this.minHeight;
25025         }
25026         
25027         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25028         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25029         
25030         if(
25031                 this.isDocument &&
25032                 (this.rotate == 0 || this.rotate == 180) && 
25033                 (
25034                     width > this.imageEl.OriginWidth || 
25035                     height > this.imageEl.OriginHeight ||
25036                     (width < this.minWidth && height < this.minHeight)
25037                 )
25038         ){
25039             return false;
25040         }
25041         
25042         if(
25043                 this.isDocument &&
25044                 (this.rotate == 90 || this.rotate == 270) && 
25045                 (
25046                     width > this.imageEl.OriginWidth || 
25047                     height > this.imageEl.OriginHeight ||
25048                     (width < this.minHeight && height < this.minWidth)
25049                 )
25050         ){
25051             return false;
25052         }
25053         
25054         if(
25055                 !this.isDocument &&
25056                 (this.rotate == 0 || this.rotate == 180) && 
25057                 (
25058                     width < this.minWidth || 
25059                     width > this.imageEl.OriginWidth || 
25060                     height < this.minHeight || 
25061                     height > this.imageEl.OriginHeight
25062                 )
25063         ){
25064             return false;
25065         }
25066         
25067         if(
25068                 !this.isDocument &&
25069                 (this.rotate == 90 || this.rotate == 270) && 
25070                 (
25071                     width < this.minHeight || 
25072                     width > this.imageEl.OriginWidth || 
25073                     height < this.minWidth || 
25074                     height > this.imageEl.OriginHeight
25075                 )
25076         ){
25077             return false;
25078         }
25079         
25080         return true;
25081         
25082     },
25083     
25084     onRotateLeft : function(e)
25085     {   
25086         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25087             
25088             var minScale = this.thumbEl.getWidth() / this.minWidth;
25089             
25090             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25091             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25092             
25093             this.startScale = this.scale;
25094             
25095             while (this.getScaleLevel() < minScale){
25096             
25097                 this.scale = this.scale + 1;
25098                 
25099                 if(!this.zoomable()){
25100                     break;
25101                 }
25102                 
25103                 if(
25104                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25105                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25106                 ){
25107                     continue;
25108                 }
25109                 
25110                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25111
25112                 this.draw();
25113                 
25114                 return;
25115             }
25116             
25117             this.scale = this.startScale;
25118             
25119             this.onRotateFail();
25120             
25121             return false;
25122         }
25123         
25124         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25125
25126         if(this.isDocument){
25127             this.setThumbBoxSize();
25128             this.setThumbBoxPosition();
25129             this.setCanvasPosition();
25130         }
25131         
25132         this.draw();
25133         
25134         this.fireEvent('rotate', this, 'left');
25135         
25136     },
25137     
25138     onRotateRight : function(e)
25139     {
25140         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25141             
25142             var minScale = this.thumbEl.getWidth() / this.minWidth;
25143         
25144             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25145             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25146             
25147             this.startScale = this.scale;
25148             
25149             while (this.getScaleLevel() < minScale){
25150             
25151                 this.scale = this.scale + 1;
25152                 
25153                 if(!this.zoomable()){
25154                     break;
25155                 }
25156                 
25157                 if(
25158                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25159                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25160                 ){
25161                     continue;
25162                 }
25163                 
25164                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25165
25166                 this.draw();
25167                 
25168                 return;
25169             }
25170             
25171             this.scale = this.startScale;
25172             
25173             this.onRotateFail();
25174             
25175             return false;
25176         }
25177         
25178         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25179
25180         if(this.isDocument){
25181             this.setThumbBoxSize();
25182             this.setThumbBoxPosition();
25183             this.setCanvasPosition();
25184         }
25185         
25186         this.draw();
25187         
25188         this.fireEvent('rotate', this, 'right');
25189     },
25190     
25191     onRotateFail : function()
25192     {
25193         this.errorEl.show(true);
25194         
25195         var _this = this;
25196         
25197         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25198     },
25199     
25200     draw : function()
25201     {
25202         this.previewEl.dom.innerHTML = '';
25203         
25204         var canvasEl = document.createElement("canvas");
25205         
25206         var contextEl = canvasEl.getContext("2d");
25207         
25208         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25209         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25210         var center = this.imageEl.OriginWidth / 2;
25211         
25212         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25213             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25214             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25215             center = this.imageEl.OriginHeight / 2;
25216         }
25217         
25218         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25219         
25220         contextEl.translate(center, center);
25221         contextEl.rotate(this.rotate * Math.PI / 180);
25222
25223         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25224         
25225         this.canvasEl = document.createElement("canvas");
25226         
25227         this.contextEl = this.canvasEl.getContext("2d");
25228         
25229         switch (this.rotate) {
25230             case 0 :
25231                 
25232                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25233                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25234                 
25235                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25236                 
25237                 break;
25238             case 90 : 
25239                 
25240                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25241                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25242                 
25243                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25244                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25245                     break;
25246                 }
25247                 
25248                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25249                 
25250                 break;
25251             case 180 :
25252                 
25253                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25254                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25255                 
25256                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25257                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25258                     break;
25259                 }
25260                 
25261                 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25262                 
25263                 break;
25264             case 270 :
25265                 
25266                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25267                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25268         
25269                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25270                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25271                     break;
25272                 }
25273                 
25274                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25275                 
25276                 break;
25277             default : 
25278                 break;
25279         }
25280         
25281         this.previewEl.appendChild(this.canvasEl);
25282         
25283         this.setCanvasPosition();
25284     },
25285     
25286     crop : function()
25287     {
25288         if(!this.canvasLoaded){
25289             return;
25290         }
25291         
25292         var imageCanvas = document.createElement("canvas");
25293         
25294         var imageContext = imageCanvas.getContext("2d");
25295         
25296         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25297         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25298         
25299         var center = imageCanvas.width / 2;
25300         
25301         imageContext.translate(center, center);
25302         
25303         imageContext.rotate(this.rotate * Math.PI / 180);
25304         
25305         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25306         
25307         var canvas = document.createElement("canvas");
25308         
25309         var context = canvas.getContext("2d");
25310                 
25311         canvas.width = this.minWidth;
25312         canvas.height = this.minHeight;
25313
25314         switch (this.rotate) {
25315             case 0 :
25316                 
25317                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25318                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25319                 
25320                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25321                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25322                 
25323                 var targetWidth = this.minWidth - 2 * x;
25324                 var targetHeight = this.minHeight - 2 * y;
25325                 
25326                 var scale = 1;
25327                 
25328                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25329                     scale = targetWidth / width;
25330                 }
25331                 
25332                 if(x > 0 && y == 0){
25333                     scale = targetHeight / height;
25334                 }
25335                 
25336                 if(x > 0 && y > 0){
25337                     scale = targetWidth / width;
25338                     
25339                     if(width < height){
25340                         scale = targetHeight / height;
25341                     }
25342                 }
25343                 
25344                 context.scale(scale, scale);
25345                 
25346                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25347                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25348
25349                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25350                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25351
25352                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25353                 
25354                 break;
25355             case 90 : 
25356                 
25357                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25358                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25359                 
25360                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25361                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25362                 
25363                 var targetWidth = this.minWidth - 2 * x;
25364                 var targetHeight = this.minHeight - 2 * y;
25365                 
25366                 var scale = 1;
25367                 
25368                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25369                     scale = targetWidth / width;
25370                 }
25371                 
25372                 if(x > 0 && y == 0){
25373                     scale = targetHeight / height;
25374                 }
25375                 
25376                 if(x > 0 && y > 0){
25377                     scale = targetWidth / width;
25378                     
25379                     if(width < height){
25380                         scale = targetHeight / height;
25381                     }
25382                 }
25383                 
25384                 context.scale(scale, scale);
25385                 
25386                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25387                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25388
25389                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25390                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25391                 
25392                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25393                 
25394                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25395                 
25396                 break;
25397             case 180 :
25398                 
25399                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25400                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25401                 
25402                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25403                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25404                 
25405                 var targetWidth = this.minWidth - 2 * x;
25406                 var targetHeight = this.minHeight - 2 * y;
25407                 
25408                 var scale = 1;
25409                 
25410                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25411                     scale = targetWidth / width;
25412                 }
25413                 
25414                 if(x > 0 && y == 0){
25415                     scale = targetHeight / height;
25416                 }
25417                 
25418                 if(x > 0 && y > 0){
25419                     scale = targetWidth / width;
25420                     
25421                     if(width < height){
25422                         scale = targetHeight / height;
25423                     }
25424                 }
25425                 
25426                 context.scale(scale, scale);
25427                 
25428                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25429                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25430
25431                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25432                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25433
25434                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25435                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25436                 
25437                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25438                 
25439                 break;
25440             case 270 :
25441                 
25442                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25443                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25444                 
25445                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25446                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25447                 
25448                 var targetWidth = this.minWidth - 2 * x;
25449                 var targetHeight = this.minHeight - 2 * y;
25450                 
25451                 var scale = 1;
25452                 
25453                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25454                     scale = targetWidth / width;
25455                 }
25456                 
25457                 if(x > 0 && y == 0){
25458                     scale = targetHeight / height;
25459                 }
25460                 
25461                 if(x > 0 && y > 0){
25462                     scale = targetWidth / width;
25463                     
25464                     if(width < height){
25465                         scale = targetHeight / height;
25466                     }
25467                 }
25468                 
25469                 context.scale(scale, scale);
25470                 
25471                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25472                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25473
25474                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25475                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25476                 
25477                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25478                 
25479                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25480                 
25481                 break;
25482             default : 
25483                 break;
25484         }
25485         
25486         this.cropData = canvas.toDataURL(this.cropType);
25487         
25488         if(this.fireEvent('crop', this, this.cropData) !== false){
25489             this.process(this.file, this.cropData);
25490         }
25491         
25492         return;
25493         
25494     },
25495     
25496     setThumbBoxSize : function()
25497     {
25498         var width, height;
25499         
25500         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25501             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25502             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25503             
25504             this.minWidth = width;
25505             this.minHeight = height;
25506             
25507             if(this.rotate == 90 || this.rotate == 270){
25508                 this.minWidth = height;
25509                 this.minHeight = width;
25510             }
25511         }
25512         
25513         height = 300;
25514         width = Math.ceil(this.minWidth * height / this.minHeight);
25515         
25516         if(this.minWidth > this.minHeight){
25517             width = 300;
25518             height = Math.ceil(this.minHeight * width / this.minWidth);
25519         }
25520         
25521         this.thumbEl.setStyle({
25522             width : width + 'px',
25523             height : height + 'px'
25524         });
25525
25526         return;
25527             
25528     },
25529     
25530     setThumbBoxPosition : function()
25531     {
25532         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25533         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25534         
25535         this.thumbEl.setLeft(x);
25536         this.thumbEl.setTop(y);
25537         
25538     },
25539     
25540     baseRotateLevel : function()
25541     {
25542         this.baseRotate = 1;
25543         
25544         if(
25545                 typeof(this.exif) != 'undefined' &&
25546                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25547                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25548         ){
25549             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25550         }
25551         
25552         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25553         
25554     },
25555     
25556     baseScaleLevel : function()
25557     {
25558         var width, height;
25559         
25560         if(this.isDocument){
25561             
25562             if(this.baseRotate == 6 || this.baseRotate == 8){
25563             
25564                 height = this.thumbEl.getHeight();
25565                 this.baseScale = height / this.imageEl.OriginWidth;
25566
25567                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25568                     width = this.thumbEl.getWidth();
25569                     this.baseScale = width / this.imageEl.OriginHeight;
25570                 }
25571
25572                 return;
25573             }
25574
25575             height = this.thumbEl.getHeight();
25576             this.baseScale = height / this.imageEl.OriginHeight;
25577
25578             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25579                 width = this.thumbEl.getWidth();
25580                 this.baseScale = width / this.imageEl.OriginWidth;
25581             }
25582
25583             return;
25584         }
25585         
25586         if(this.baseRotate == 6 || this.baseRotate == 8){
25587             
25588             width = this.thumbEl.getHeight();
25589             this.baseScale = width / this.imageEl.OriginHeight;
25590             
25591             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25592                 height = this.thumbEl.getWidth();
25593                 this.baseScale = height / this.imageEl.OriginHeight;
25594             }
25595             
25596             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25597                 height = this.thumbEl.getWidth();
25598                 this.baseScale = height / this.imageEl.OriginHeight;
25599                 
25600                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25601                     width = this.thumbEl.getHeight();
25602                     this.baseScale = width / this.imageEl.OriginWidth;
25603                 }
25604             }
25605             
25606             return;
25607         }
25608         
25609         width = this.thumbEl.getWidth();
25610         this.baseScale = width / this.imageEl.OriginWidth;
25611         
25612         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25613             height = this.thumbEl.getHeight();
25614             this.baseScale = height / this.imageEl.OriginHeight;
25615         }
25616         
25617         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25618             
25619             height = this.thumbEl.getHeight();
25620             this.baseScale = height / this.imageEl.OriginHeight;
25621             
25622             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25623                 width = this.thumbEl.getWidth();
25624                 this.baseScale = width / this.imageEl.OriginWidth;
25625             }
25626             
25627         }
25628         
25629         return;
25630     },
25631     
25632     getScaleLevel : function()
25633     {
25634         return this.baseScale * Math.pow(1.1, this.scale);
25635     },
25636     
25637     onTouchStart : function(e)
25638     {
25639         if(!this.canvasLoaded){
25640             this.beforeSelectFile(e);
25641             return;
25642         }
25643         
25644         var touches = e.browserEvent.touches;
25645         
25646         if(!touches){
25647             return;
25648         }
25649         
25650         if(touches.length == 1){
25651             this.onMouseDown(e);
25652             return;
25653         }
25654         
25655         if(touches.length != 2){
25656             return;
25657         }
25658         
25659         var coords = [];
25660         
25661         for(var i = 0, finger; finger = touches[i]; i++){
25662             coords.push(finger.pageX, finger.pageY);
25663         }
25664         
25665         var x = Math.pow(coords[0] - coords[2], 2);
25666         var y = Math.pow(coords[1] - coords[3], 2);
25667         
25668         this.startDistance = Math.sqrt(x + y);
25669         
25670         this.startScale = this.scale;
25671         
25672         this.pinching = true;
25673         this.dragable = false;
25674         
25675     },
25676     
25677     onTouchMove : function(e)
25678     {
25679         if(!this.pinching && !this.dragable){
25680             return;
25681         }
25682         
25683         var touches = e.browserEvent.touches;
25684         
25685         if(!touches){
25686             return;
25687         }
25688         
25689         if(this.dragable){
25690             this.onMouseMove(e);
25691             return;
25692         }
25693         
25694         var coords = [];
25695         
25696         for(var i = 0, finger; finger = touches[i]; i++){
25697             coords.push(finger.pageX, finger.pageY);
25698         }
25699         
25700         var x = Math.pow(coords[0] - coords[2], 2);
25701         var y = Math.pow(coords[1] - coords[3], 2);
25702         
25703         this.endDistance = Math.sqrt(x + y);
25704         
25705         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25706         
25707         if(!this.zoomable()){
25708             this.scale = this.startScale;
25709             return;
25710         }
25711         
25712         this.draw();
25713         
25714     },
25715     
25716     onTouchEnd : function(e)
25717     {
25718         this.pinching = false;
25719         this.dragable = false;
25720         
25721     },
25722     
25723     process : function(file, crop)
25724     {
25725         if(this.loadMask){
25726             this.maskEl.mask(this.loadingText);
25727         }
25728         
25729         this.xhr = new XMLHttpRequest();
25730         
25731         file.xhr = this.xhr;
25732
25733         this.xhr.open(this.method, this.url, true);
25734         
25735         var headers = {
25736             "Accept": "application/json",
25737             "Cache-Control": "no-cache",
25738             "X-Requested-With": "XMLHttpRequest"
25739         };
25740         
25741         for (var headerName in headers) {
25742             var headerValue = headers[headerName];
25743             if (headerValue) {
25744                 this.xhr.setRequestHeader(headerName, headerValue);
25745             }
25746         }
25747         
25748         var _this = this;
25749         
25750         this.xhr.onload = function()
25751         {
25752             _this.xhrOnLoad(_this.xhr);
25753         }
25754         
25755         this.xhr.onerror = function()
25756         {
25757             _this.xhrOnError(_this.xhr);
25758         }
25759         
25760         var formData = new FormData();
25761
25762         formData.append('returnHTML', 'NO');
25763         
25764         if(crop){
25765             formData.append('crop', crop);
25766         }
25767         
25768         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25769             formData.append(this.paramName, file, file.name);
25770         }
25771         
25772         if(typeof(file.filename) != 'undefined'){
25773             formData.append('filename', file.filename);
25774         }
25775         
25776         if(typeof(file.mimetype) != 'undefined'){
25777             formData.append('mimetype', file.mimetype);
25778         }
25779         
25780         if(this.fireEvent('arrange', this, formData) != false){
25781             this.xhr.send(formData);
25782         };
25783     },
25784     
25785     xhrOnLoad : function(xhr)
25786     {
25787         if(this.loadMask){
25788             this.maskEl.unmask();
25789         }
25790         
25791         if (xhr.readyState !== 4) {
25792             this.fireEvent('exception', this, xhr);
25793             return;
25794         }
25795
25796         var response = Roo.decode(xhr.responseText);
25797         
25798         if(!response.success){
25799             this.fireEvent('exception', this, xhr);
25800             return;
25801         }
25802         
25803         var response = Roo.decode(xhr.responseText);
25804         
25805         this.fireEvent('upload', this, response);
25806         
25807     },
25808     
25809     xhrOnError : function()
25810     {
25811         if(this.loadMask){
25812             this.maskEl.unmask();
25813         }
25814         
25815         Roo.log('xhr on error');
25816         
25817         var response = Roo.decode(xhr.responseText);
25818           
25819         Roo.log(response);
25820         
25821     },
25822     
25823     prepare : function(file)
25824     {   
25825         if(this.loadMask){
25826             this.maskEl.mask(this.loadingText);
25827         }
25828         
25829         this.file = false;
25830         this.exif = {};
25831         
25832         if(typeof(file) === 'string'){
25833             this.loadCanvas(file);
25834             return;
25835         }
25836         
25837         if(!file || !this.urlAPI){
25838             return;
25839         }
25840         
25841         this.file = file;
25842         this.cropType = file.type;
25843         
25844         var _this = this;
25845         
25846         if(this.fireEvent('prepare', this, this.file) != false){
25847             
25848             var reader = new FileReader();
25849             
25850             reader.onload = function (e) {
25851                 if (e.target.error) {
25852                     Roo.log(e.target.error);
25853                     return;
25854                 }
25855                 
25856                 var buffer = e.target.result,
25857                     dataView = new DataView(buffer),
25858                     offset = 2,
25859                     maxOffset = dataView.byteLength - 4,
25860                     markerBytes,
25861                     markerLength;
25862                 
25863                 if (dataView.getUint16(0) === 0xffd8) {
25864                     while (offset < maxOffset) {
25865                         markerBytes = dataView.getUint16(offset);
25866                         
25867                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25868                             markerLength = dataView.getUint16(offset + 2) + 2;
25869                             if (offset + markerLength > dataView.byteLength) {
25870                                 Roo.log('Invalid meta data: Invalid segment size.');
25871                                 break;
25872                             }
25873                             
25874                             if(markerBytes == 0xffe1){
25875                                 _this.parseExifData(
25876                                     dataView,
25877                                     offset,
25878                                     markerLength
25879                                 );
25880                             }
25881                             
25882                             offset += markerLength;
25883                             
25884                             continue;
25885                         }
25886                         
25887                         break;
25888                     }
25889                     
25890                 }
25891                 
25892                 var url = _this.urlAPI.createObjectURL(_this.file);
25893                 
25894                 _this.loadCanvas(url);
25895                 
25896                 return;
25897             }
25898             
25899             reader.readAsArrayBuffer(this.file);
25900             
25901         }
25902         
25903     },
25904     
25905     parseExifData : function(dataView, offset, length)
25906     {
25907         var tiffOffset = offset + 10,
25908             littleEndian,
25909             dirOffset;
25910     
25911         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25912             // No Exif data, might be XMP data instead
25913             return;
25914         }
25915         
25916         // Check for the ASCII code for "Exif" (0x45786966):
25917         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25918             // No Exif data, might be XMP data instead
25919             return;
25920         }
25921         if (tiffOffset + 8 > dataView.byteLength) {
25922             Roo.log('Invalid Exif data: Invalid segment size.');
25923             return;
25924         }
25925         // Check for the two null bytes:
25926         if (dataView.getUint16(offset + 8) !== 0x0000) {
25927             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25928             return;
25929         }
25930         // Check the byte alignment:
25931         switch (dataView.getUint16(tiffOffset)) {
25932         case 0x4949:
25933             littleEndian = true;
25934             break;
25935         case 0x4D4D:
25936             littleEndian = false;
25937             break;
25938         default:
25939             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25940             return;
25941         }
25942         // Check for the TIFF tag marker (0x002A):
25943         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25944             Roo.log('Invalid Exif data: Missing TIFF marker.');
25945             return;
25946         }
25947         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25948         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25949         
25950         this.parseExifTags(
25951             dataView,
25952             tiffOffset,
25953             tiffOffset + dirOffset,
25954             littleEndian
25955         );
25956     },
25957     
25958     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25959     {
25960         var tagsNumber,
25961             dirEndOffset,
25962             i;
25963         if (dirOffset + 6 > dataView.byteLength) {
25964             Roo.log('Invalid Exif data: Invalid directory offset.');
25965             return;
25966         }
25967         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25968         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25969         if (dirEndOffset + 4 > dataView.byteLength) {
25970             Roo.log('Invalid Exif data: Invalid directory size.');
25971             return;
25972         }
25973         for (i = 0; i < tagsNumber; i += 1) {
25974             this.parseExifTag(
25975                 dataView,
25976                 tiffOffset,
25977                 dirOffset + 2 + 12 * i, // tag offset
25978                 littleEndian
25979             );
25980         }
25981         // Return the offset to the next directory:
25982         return dataView.getUint32(dirEndOffset, littleEndian);
25983     },
25984     
25985     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25986     {
25987         var tag = dataView.getUint16(offset, littleEndian);
25988         
25989         this.exif[tag] = this.getExifValue(
25990             dataView,
25991             tiffOffset,
25992             offset,
25993             dataView.getUint16(offset + 2, littleEndian), // tag type
25994             dataView.getUint32(offset + 4, littleEndian), // tag length
25995             littleEndian
25996         );
25997     },
25998     
25999     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26000     {
26001         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26002             tagSize,
26003             dataOffset,
26004             values,
26005             i,
26006             str,
26007             c;
26008     
26009         if (!tagType) {
26010             Roo.log('Invalid Exif data: Invalid tag type.');
26011             return;
26012         }
26013         
26014         tagSize = tagType.size * length;
26015         // Determine if the value is contained in the dataOffset bytes,
26016         // or if the value at the dataOffset is a pointer to the actual data:
26017         dataOffset = tagSize > 4 ?
26018                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26019         if (dataOffset + tagSize > dataView.byteLength) {
26020             Roo.log('Invalid Exif data: Invalid data offset.');
26021             return;
26022         }
26023         if (length === 1) {
26024             return tagType.getValue(dataView, dataOffset, littleEndian);
26025         }
26026         values = [];
26027         for (i = 0; i < length; i += 1) {
26028             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26029         }
26030         
26031         if (tagType.ascii) {
26032             str = '';
26033             // Concatenate the chars:
26034             for (i = 0; i < values.length; i += 1) {
26035                 c = values[i];
26036                 // Ignore the terminating NULL byte(s):
26037                 if (c === '\u0000') {
26038                     break;
26039                 }
26040                 str += c;
26041             }
26042             return str;
26043         }
26044         return values;
26045     }
26046     
26047 });
26048
26049 Roo.apply(Roo.bootstrap.UploadCropbox, {
26050     tags : {
26051         'Orientation': 0x0112
26052     },
26053     
26054     Orientation: {
26055             1: 0, //'top-left',
26056 //            2: 'top-right',
26057             3: 180, //'bottom-right',
26058 //            4: 'bottom-left',
26059 //            5: 'left-top',
26060             6: 90, //'right-top',
26061 //            7: 'right-bottom',
26062             8: 270 //'left-bottom'
26063     },
26064     
26065     exifTagTypes : {
26066         // byte, 8-bit unsigned int:
26067         1: {
26068             getValue: function (dataView, dataOffset) {
26069                 return dataView.getUint8(dataOffset);
26070             },
26071             size: 1
26072         },
26073         // ascii, 8-bit byte:
26074         2: {
26075             getValue: function (dataView, dataOffset) {
26076                 return String.fromCharCode(dataView.getUint8(dataOffset));
26077             },
26078             size: 1,
26079             ascii: true
26080         },
26081         // short, 16 bit int:
26082         3: {
26083             getValue: function (dataView, dataOffset, littleEndian) {
26084                 return dataView.getUint16(dataOffset, littleEndian);
26085             },
26086             size: 2
26087         },
26088         // long, 32 bit int:
26089         4: {
26090             getValue: function (dataView, dataOffset, littleEndian) {
26091                 return dataView.getUint32(dataOffset, littleEndian);
26092             },
26093             size: 4
26094         },
26095         // rational = two long values, first is numerator, second is denominator:
26096         5: {
26097             getValue: function (dataView, dataOffset, littleEndian) {
26098                 return dataView.getUint32(dataOffset, littleEndian) /
26099                     dataView.getUint32(dataOffset + 4, littleEndian);
26100             },
26101             size: 8
26102         },
26103         // slong, 32 bit signed int:
26104         9: {
26105             getValue: function (dataView, dataOffset, littleEndian) {
26106                 return dataView.getInt32(dataOffset, littleEndian);
26107             },
26108             size: 4
26109         },
26110         // srational, two slongs, first is numerator, second is denominator:
26111         10: {
26112             getValue: function (dataView, dataOffset, littleEndian) {
26113                 return dataView.getInt32(dataOffset, littleEndian) /
26114                     dataView.getInt32(dataOffset + 4, littleEndian);
26115             },
26116             size: 8
26117         }
26118     },
26119     
26120     footer : {
26121         STANDARD : [
26122             {
26123                 tag : 'div',
26124                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26125                 action : 'rotate-left',
26126                 cn : [
26127                     {
26128                         tag : 'button',
26129                         cls : 'btn btn-default',
26130                         html : '<i class="fa fa-undo"></i>'
26131                     }
26132                 ]
26133             },
26134             {
26135                 tag : 'div',
26136                 cls : 'btn-group roo-upload-cropbox-picture',
26137                 action : 'picture',
26138                 cn : [
26139                     {
26140                         tag : 'button',
26141                         cls : 'btn btn-default',
26142                         html : '<i class="fa fa-picture-o"></i>'
26143                     }
26144                 ]
26145             },
26146             {
26147                 tag : 'div',
26148                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26149                 action : 'rotate-right',
26150                 cn : [
26151                     {
26152                         tag : 'button',
26153                         cls : 'btn btn-default',
26154                         html : '<i class="fa fa-repeat"></i>'
26155                     }
26156                 ]
26157             }
26158         ],
26159         DOCUMENT : [
26160             {
26161                 tag : 'div',
26162                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26163                 action : 'rotate-left',
26164                 cn : [
26165                     {
26166                         tag : 'button',
26167                         cls : 'btn btn-default',
26168                         html : '<i class="fa fa-undo"></i>'
26169                     }
26170                 ]
26171             },
26172             {
26173                 tag : 'div',
26174                 cls : 'btn-group roo-upload-cropbox-download',
26175                 action : 'download',
26176                 cn : [
26177                     {
26178                         tag : 'button',
26179                         cls : 'btn btn-default',
26180                         html : '<i class="fa fa-download"></i>'
26181                     }
26182                 ]
26183             },
26184             {
26185                 tag : 'div',
26186                 cls : 'btn-group roo-upload-cropbox-crop',
26187                 action : 'crop',
26188                 cn : [
26189                     {
26190                         tag : 'button',
26191                         cls : 'btn btn-default',
26192                         html : '<i class="fa fa-crop"></i>'
26193                     }
26194                 ]
26195             },
26196             {
26197                 tag : 'div',
26198                 cls : 'btn-group roo-upload-cropbox-trash',
26199                 action : 'trash',
26200                 cn : [
26201                     {
26202                         tag : 'button',
26203                         cls : 'btn btn-default',
26204                         html : '<i class="fa fa-trash"></i>'
26205                     }
26206                 ]
26207             },
26208             {
26209                 tag : 'div',
26210                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26211                 action : 'rotate-right',
26212                 cn : [
26213                     {
26214                         tag : 'button',
26215                         cls : 'btn btn-default',
26216                         html : '<i class="fa fa-repeat"></i>'
26217                     }
26218                 ]
26219             }
26220         ],
26221         ROTATOR : [
26222             {
26223                 tag : 'div',
26224                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26225                 action : 'rotate-left',
26226                 cn : [
26227                     {
26228                         tag : 'button',
26229                         cls : 'btn btn-default',
26230                         html : '<i class="fa fa-undo"></i>'
26231                     }
26232                 ]
26233             },
26234             {
26235                 tag : 'div',
26236                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26237                 action : 'rotate-right',
26238                 cn : [
26239                     {
26240                         tag : 'button',
26241                         cls : 'btn btn-default',
26242                         html : '<i class="fa fa-repeat"></i>'
26243                     }
26244                 ]
26245             }
26246         ]
26247     }
26248 });
26249
26250 /*
26251 * Licence: LGPL
26252 */
26253
26254 /**
26255  * @class Roo.bootstrap.DocumentManager
26256  * @extends Roo.bootstrap.Component
26257  * Bootstrap DocumentManager class
26258  * @cfg {String} paramName default 'imageUpload'
26259  * @cfg {String} method default POST
26260  * @cfg {String} url action url
26261  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26262  * @cfg {Boolean} multiple multiple upload default true
26263  * @cfg {Number} thumbSize default 300
26264  * @cfg {String} fieldLabel
26265  * @cfg {Number} labelWidth default 4
26266  * @cfg {String} labelAlign (left|top) default left
26267  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26268  * 
26269  * @constructor
26270  * Create a new DocumentManager
26271  * @param {Object} config The config object
26272  */
26273
26274 Roo.bootstrap.DocumentManager = function(config){
26275     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26276     
26277     this.addEvents({
26278         /**
26279          * @event initial
26280          * Fire when initial the DocumentManager
26281          * @param {Roo.bootstrap.DocumentManager} this
26282          */
26283         "initial" : true,
26284         /**
26285          * @event inspect
26286          * inspect selected file
26287          * @param {Roo.bootstrap.DocumentManager} this
26288          * @param {File} file
26289          */
26290         "inspect" : true,
26291         /**
26292          * @event exception
26293          * Fire when xhr load exception
26294          * @param {Roo.bootstrap.DocumentManager} this
26295          * @param {XMLHttpRequest} xhr
26296          */
26297         "exception" : true,
26298         /**
26299          * @event prepare
26300          * prepare the form data
26301          * @param {Roo.bootstrap.DocumentManager} this
26302          * @param {Object} formData
26303          */
26304         "prepare" : true,
26305         /**
26306          * @event remove
26307          * Fire when remove the file
26308          * @param {Roo.bootstrap.DocumentManager} this
26309          * @param {Object} file
26310          */
26311         "remove" : true,
26312         /**
26313          * @event refresh
26314          * Fire after refresh the file
26315          * @param {Roo.bootstrap.DocumentManager} this
26316          */
26317         "refresh" : true,
26318         /**
26319          * @event click
26320          * Fire after click the image
26321          * @param {Roo.bootstrap.DocumentManager} this
26322          * @param {Object} file
26323          */
26324         "click" : true,
26325         /**
26326          * @event edit
26327          * Fire when upload a image and editable set to true
26328          * @param {Roo.bootstrap.DocumentManager} this
26329          * @param {Object} file
26330          */
26331         "edit" : true,
26332         /**
26333          * @event beforeselectfile
26334          * Fire before select file
26335          * @param {Roo.bootstrap.DocumentManager} this
26336          */
26337         "beforeselectfile" : true,
26338         /**
26339          * @event process
26340          * Fire before process file
26341          * @param {Roo.bootstrap.DocumentManager} this
26342          * @param {Object} file
26343          */
26344         "process" : true
26345         
26346     });
26347 };
26348
26349 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26350     
26351     boxes : 0,
26352     inputName : '',
26353     thumbSize : 300,
26354     multiple : true,
26355     files : [],
26356     method : 'POST',
26357     url : '',
26358     paramName : 'imageUpload',
26359     fieldLabel : '',
26360     labelWidth : 4,
26361     labelAlign : 'left',
26362     editable : true,
26363     delegates : [],
26364     
26365     
26366     xhr : false, 
26367     
26368     getAutoCreate : function()
26369     {   
26370         var managerWidget = {
26371             tag : 'div',
26372             cls : 'roo-document-manager',
26373             cn : [
26374                 {
26375                     tag : 'input',
26376                     cls : 'roo-document-manager-selector',
26377                     type : 'file'
26378                 },
26379                 {
26380                     tag : 'div',
26381                     cls : 'roo-document-manager-uploader',
26382                     cn : [
26383                         {
26384                             tag : 'div',
26385                             cls : 'roo-document-manager-upload-btn',
26386                             html : '<i class="fa fa-plus"></i>'
26387                         }
26388                     ]
26389                     
26390                 }
26391             ]
26392         };
26393         
26394         var content = [
26395             {
26396                 tag : 'div',
26397                 cls : 'column col-md-12',
26398                 cn : managerWidget
26399             }
26400         ];
26401         
26402         if(this.fieldLabel.length){
26403             
26404             content = [
26405                 {
26406                     tag : 'div',
26407                     cls : 'column col-md-12',
26408                     html : this.fieldLabel
26409                 },
26410                 {
26411                     tag : 'div',
26412                     cls : 'column col-md-12',
26413                     cn : managerWidget
26414                 }
26415             ];
26416
26417             if(this.labelAlign == 'left'){
26418                 content = [
26419                     {
26420                         tag : 'div',
26421                         cls : 'column col-md-' + this.labelWidth,
26422                         html : this.fieldLabel
26423                     },
26424                     {
26425                         tag : 'div',
26426                         cls : 'column col-md-' + (12 - this.labelWidth),
26427                         cn : managerWidget
26428                     }
26429                 ];
26430                 
26431             }
26432         }
26433         
26434         var cfg = {
26435             tag : 'div',
26436             cls : 'row clearfix',
26437             cn : content
26438         };
26439         
26440         return cfg;
26441         
26442     },
26443     
26444     initEvents : function()
26445     {
26446         this.managerEl = this.el.select('.roo-document-manager', true).first();
26447         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26448         
26449         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26450         this.selectorEl.hide();
26451         
26452         if(this.multiple){
26453             this.selectorEl.attr('multiple', 'multiple');
26454         }
26455         
26456         this.selectorEl.on('change', this.onFileSelected, this);
26457         
26458         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26459         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26460         
26461         this.uploader.on('click', this.onUploaderClick, this);
26462         
26463         this.renderProgressDialog();
26464         
26465         var _this = this;
26466         
26467         window.addEventListener("resize", function() { _this.refresh(); } );
26468         
26469         this.fireEvent('initial', this);
26470     },
26471     
26472     renderProgressDialog : function()
26473     {
26474         var _this = this;
26475         
26476         this.progressDialog = new Roo.bootstrap.Modal({
26477             cls : 'roo-document-manager-progress-dialog',
26478             allow_close : false,
26479             title : '',
26480             buttons : [
26481                 {
26482                     name  :'cancel',
26483                     weight : 'danger',
26484                     html : 'Cancel'
26485                 }
26486             ], 
26487             listeners : { 
26488                 btnclick : function() {
26489                     _this.uploadCancel();
26490                     this.hide();
26491                 }
26492             }
26493         });
26494          
26495         this.progressDialog.render(Roo.get(document.body));
26496          
26497         this.progress = new Roo.bootstrap.Progress({
26498             cls : 'roo-document-manager-progress',
26499             active : true,
26500             striped : true
26501         });
26502         
26503         this.progress.render(this.progressDialog.getChildContainer());
26504         
26505         this.progressBar = new Roo.bootstrap.ProgressBar({
26506             cls : 'roo-document-manager-progress-bar',
26507             aria_valuenow : 0,
26508             aria_valuemin : 0,
26509             aria_valuemax : 12,
26510             panel : 'success'
26511         });
26512         
26513         this.progressBar.render(this.progress.getChildContainer());
26514     },
26515     
26516     onUploaderClick : function(e)
26517     {
26518         e.preventDefault();
26519      
26520         if(this.fireEvent('beforeselectfile', this) != false){
26521             this.selectorEl.dom.click();
26522         }
26523         
26524     },
26525     
26526     onFileSelected : function(e)
26527     {
26528         e.preventDefault();
26529         
26530         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26531             return;
26532         }
26533         
26534         Roo.each(this.selectorEl.dom.files, function(file){
26535             if(this.fireEvent('inspect', this, file) != false){
26536                 this.files.push(file);
26537             }
26538         }, this);
26539         
26540         this.queue();
26541         
26542     },
26543     
26544     queue : function()
26545     {
26546         this.selectorEl.dom.value = '';
26547         
26548         if(!this.files.length){
26549             return;
26550         }
26551         
26552         if(this.boxes > 0 && this.files.length > this.boxes){
26553             this.files = this.files.slice(0, this.boxes);
26554         }
26555         
26556         this.uploader.show();
26557         
26558         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26559             this.uploader.hide();
26560         }
26561         
26562         var _this = this;
26563         
26564         var files = [];
26565         
26566         var docs = [];
26567         
26568         Roo.each(this.files, function(file){
26569             
26570             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26571                 var f = this.renderPreview(file);
26572                 files.push(f);
26573                 return;
26574             }
26575             
26576             if(file.type.indexOf('image') != -1){
26577                 this.delegates.push(
26578                     (function(){
26579                         _this.process(file);
26580                     }).createDelegate(this)
26581                 );
26582         
26583                 return;
26584             }
26585             
26586             docs.push(
26587                 (function(){
26588                     _this.process(file);
26589                 }).createDelegate(this)
26590             );
26591             
26592         }, this);
26593         
26594         this.files = files;
26595         
26596         this.delegates = this.delegates.concat(docs);
26597         
26598         if(!this.delegates.length){
26599             this.refresh();
26600             return;
26601         }
26602         
26603         this.progressBar.aria_valuemax = this.delegates.length;
26604         
26605         this.arrange();
26606         
26607         return;
26608     },
26609     
26610     arrange : function()
26611     {
26612         if(!this.delegates.length){
26613             this.progressDialog.hide();
26614             this.refresh();
26615             return;
26616         }
26617         
26618         var delegate = this.delegates.shift();
26619         
26620         this.progressDialog.show();
26621         
26622         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26623         
26624         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26625         
26626         delegate();
26627     },
26628     
26629     refresh : function()
26630     {
26631         this.uploader.show();
26632         
26633         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26634             this.uploader.hide();
26635         }
26636         
26637         Roo.isTouch ? this.closable(false) : this.closable(true);
26638         
26639         this.fireEvent('refresh', this);
26640     },
26641     
26642     onRemove : function(e, el, o)
26643     {
26644         e.preventDefault();
26645         
26646         this.fireEvent('remove', this, o);
26647         
26648     },
26649     
26650     remove : function(o)
26651     {
26652         var files = [];
26653         
26654         Roo.each(this.files, function(file){
26655             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26656                 files.push(file);
26657                 return;
26658             }
26659
26660             o.target.remove();
26661
26662         }, this);
26663         
26664         this.files = files;
26665         
26666         this.refresh();
26667     },
26668     
26669     clear : function()
26670     {
26671         Roo.each(this.files, function(file){
26672             if(!file.target){
26673                 return;
26674             }
26675             
26676             file.target.remove();
26677
26678         }, this);
26679         
26680         this.files = [];
26681         
26682         this.refresh();
26683     },
26684     
26685     onClick : function(e, el, o)
26686     {
26687         e.preventDefault();
26688         
26689         this.fireEvent('click', this, o);
26690         
26691     },
26692     
26693     closable : function(closable)
26694     {
26695         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26696             
26697             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26698             
26699             if(closable){
26700                 el.show();
26701                 return;
26702             }
26703             
26704             el.hide();
26705             
26706         }, this);
26707     },
26708     
26709     xhrOnLoad : function(xhr)
26710     {
26711         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26712             el.remove();
26713         }, this);
26714         
26715         if (xhr.readyState !== 4) {
26716             this.arrange();
26717             this.fireEvent('exception', this, xhr);
26718             return;
26719         }
26720
26721         var response = Roo.decode(xhr.responseText);
26722         
26723         if(!response.success){
26724             this.arrange();
26725             this.fireEvent('exception', this, xhr);
26726             return;
26727         }
26728         
26729         var file = this.renderPreview(response.data);
26730         
26731         this.files.push(file);
26732         
26733         this.arrange();
26734         
26735     },
26736     
26737     xhrOnError : function()
26738     {
26739         Roo.log('xhr on error');
26740         
26741         var response = Roo.decode(xhr.responseText);
26742           
26743         Roo.log(response);
26744         
26745         this.arrange();
26746     },
26747     
26748     process : function(file)
26749     {
26750         if(this.fireEvent('process', this, file) !== false){
26751             if(this.editable && file.type.indexOf('image') != -1){
26752                 this.fireEvent('edit', this, file);
26753                 return;
26754             }
26755
26756             this.uploadStart(file, false);
26757
26758             return;
26759         }
26760         
26761     },
26762     
26763     uploadStart : function(file, crop)
26764     {
26765         this.xhr = new XMLHttpRequest();
26766         
26767         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26768             this.arrange();
26769             return;
26770         }
26771         
26772         file.xhr = this.xhr;
26773             
26774         this.managerEl.createChild({
26775             tag : 'div',
26776             cls : 'roo-document-manager-loading',
26777             cn : [
26778                 {
26779                     tag : 'div',
26780                     tooltip : file.name,
26781                     cls : 'roo-document-manager-thumb',
26782                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26783                 }
26784             ]
26785
26786         });
26787
26788         this.xhr.open(this.method, this.url, true);
26789         
26790         var headers = {
26791             "Accept": "application/json",
26792             "Cache-Control": "no-cache",
26793             "X-Requested-With": "XMLHttpRequest"
26794         };
26795         
26796         for (var headerName in headers) {
26797             var headerValue = headers[headerName];
26798             if (headerValue) {
26799                 this.xhr.setRequestHeader(headerName, headerValue);
26800             }
26801         }
26802         
26803         var _this = this;
26804         
26805         this.xhr.onload = function()
26806         {
26807             _this.xhrOnLoad(_this.xhr);
26808         }
26809         
26810         this.xhr.onerror = function()
26811         {
26812             _this.xhrOnError(_this.xhr);
26813         }
26814         
26815         var formData = new FormData();
26816
26817         formData.append('returnHTML', 'NO');
26818         
26819         if(crop){
26820             formData.append('crop', crop);
26821         }
26822         
26823         formData.append(this.paramName, file, file.name);
26824         
26825         if(this.fireEvent('prepare', this, formData) != false){
26826             this.xhr.send(formData);
26827         };
26828     },
26829     
26830     uploadCancel : function()
26831     {
26832         if (this.xhr) {
26833             this.xhr.abort();
26834         }
26835         
26836         
26837         this.delegates = [];
26838         
26839         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26840             el.remove();
26841         }, this);
26842         
26843         this.arrange();
26844     },
26845     
26846     renderPreview : function(file)
26847     {
26848         if(typeof(file.target) != 'undefined' && file.target){
26849             return file;
26850         }
26851         
26852         var previewEl = this.managerEl.createChild({
26853             tag : 'div',
26854             cls : 'roo-document-manager-preview',
26855             cn : [
26856                 {
26857                     tag : 'div',
26858                     tooltip : file.filename,
26859                     cls : 'roo-document-manager-thumb',
26860                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26861                 },
26862                 {
26863                     tag : 'button',
26864                     cls : 'close',
26865                     html : '<i class="fa fa-times-circle"></i>'
26866                 }
26867             ]
26868         });
26869
26870         var close = previewEl.select('button.close', true).first();
26871
26872         close.on('click', this.onRemove, this, file);
26873
26874         file.target = previewEl;
26875
26876         var image = previewEl.select('img', true).first();
26877         
26878         var _this = this;
26879         
26880         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26881         
26882         image.on('click', this.onClick, this, file);
26883         
26884         return file;
26885         
26886     },
26887     
26888     onPreviewLoad : function(file, image)
26889     {
26890         if(typeof(file.target) == 'undefined' || !file.target){
26891             return;
26892         }
26893         
26894         var width = image.dom.naturalWidth || image.dom.width;
26895         var height = image.dom.naturalHeight || image.dom.height;
26896         
26897         if(width > height){
26898             file.target.addClass('wide');
26899             return;
26900         }
26901         
26902         file.target.addClass('tall');
26903         return;
26904         
26905     },
26906     
26907     uploadFromSource : function(file, crop)
26908     {
26909         this.xhr = new XMLHttpRequest();
26910         
26911         this.managerEl.createChild({
26912             tag : 'div',
26913             cls : 'roo-document-manager-loading',
26914             cn : [
26915                 {
26916                     tag : 'div',
26917                     tooltip : file.name,
26918                     cls : 'roo-document-manager-thumb',
26919                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26920                 }
26921             ]
26922
26923         });
26924
26925         this.xhr.open(this.method, this.url, true);
26926         
26927         var headers = {
26928             "Accept": "application/json",
26929             "Cache-Control": "no-cache",
26930             "X-Requested-With": "XMLHttpRequest"
26931         };
26932         
26933         for (var headerName in headers) {
26934             var headerValue = headers[headerName];
26935             if (headerValue) {
26936                 this.xhr.setRequestHeader(headerName, headerValue);
26937             }
26938         }
26939         
26940         var _this = this;
26941         
26942         this.xhr.onload = function()
26943         {
26944             _this.xhrOnLoad(_this.xhr);
26945         }
26946         
26947         this.xhr.onerror = function()
26948         {
26949             _this.xhrOnError(_this.xhr);
26950         }
26951         
26952         var formData = new FormData();
26953
26954         formData.append('returnHTML', 'NO');
26955         
26956         formData.append('crop', crop);
26957         
26958         if(typeof(file.filename) != 'undefined'){
26959             formData.append('filename', file.filename);
26960         }
26961         
26962         if(typeof(file.mimetype) != 'undefined'){
26963             formData.append('mimetype', file.mimetype);
26964         }
26965         
26966         if(this.fireEvent('prepare', this, formData) != false){
26967             this.xhr.send(formData);
26968         };
26969     }
26970 });
26971
26972 /*
26973 * Licence: LGPL
26974 */
26975
26976 /**
26977  * @class Roo.bootstrap.DocumentViewer
26978  * @extends Roo.bootstrap.Component
26979  * Bootstrap DocumentViewer class
26980  * 
26981  * @constructor
26982  * Create a new DocumentViewer
26983  * @param {Object} config The config object
26984  */
26985
26986 Roo.bootstrap.DocumentViewer = function(config){
26987     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26988     
26989     this.addEvents({
26990         /**
26991          * @event initial
26992          * Fire after initEvent
26993          * @param {Roo.bootstrap.DocumentViewer} this
26994          */
26995         "initial" : true,
26996         /**
26997          * @event click
26998          * Fire after click
26999          * @param {Roo.bootstrap.DocumentViewer} this
27000          */
27001         "click" : true,
27002         /**
27003          * @event trash
27004          * Fire after trash button
27005          * @param {Roo.bootstrap.DocumentViewer} this
27006          */
27007         "trash" : true
27008         
27009     });
27010 };
27011
27012 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27013     
27014     getAutoCreate : function()
27015     {
27016         var cfg = {
27017             tag : 'div',
27018             cls : 'roo-document-viewer',
27019             cn : [
27020                 {
27021                     tag : 'div',
27022                     cls : 'roo-document-viewer-body',
27023                     cn : [
27024                         {
27025                             tag : 'div',
27026                             cls : 'roo-document-viewer-thumb',
27027                             cn : [
27028                                 {
27029                                     tag : 'img',
27030                                     cls : 'roo-document-viewer-image'
27031                                 }
27032                             ]
27033                         }
27034                     ]
27035                 },
27036                 {
27037                     tag : 'div',
27038                     cls : 'roo-document-viewer-footer',
27039                     cn : {
27040                         tag : 'div',
27041                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27042                         cn : [
27043                             {
27044                                 tag : 'div',
27045                                 cls : 'btn-group',
27046                                 cn : [
27047                                     {
27048                                         tag : 'button',
27049                                         cls : 'btn btn-default roo-document-viewer-trash',
27050                                         html : '<i class="fa fa-trash"></i>'
27051                                     }
27052                                 ]
27053                             }
27054                         ]
27055                     }
27056                 }
27057             ]
27058         };
27059         
27060         return cfg;
27061     },
27062     
27063     initEvents : function()
27064     {
27065         
27066         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27067         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27068         
27069         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27070         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27071         
27072         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27073         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27074         
27075         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27076         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27077         
27078         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27079         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27080         
27081         this.bodyEl.on('click', this.onClick, this);
27082         
27083         this.trashBtn.on('click', this.onTrash, this);
27084         
27085     },
27086     
27087     initial : function()
27088     {
27089 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27090         
27091         
27092         this.fireEvent('initial', this);
27093         
27094     },
27095     
27096     onClick : function(e)
27097     {
27098         e.preventDefault();
27099         
27100         this.fireEvent('click', this);
27101     },
27102     
27103     onTrash : function(e)
27104     {
27105         e.preventDefault();
27106         
27107         this.fireEvent('trash', this);
27108     }
27109     
27110 });
27111 /*
27112  * - LGPL
27113  *
27114  * nav progress bar
27115  * 
27116  */
27117
27118 /**
27119  * @class Roo.bootstrap.NavProgressBar
27120  * @extends Roo.bootstrap.Component
27121  * Bootstrap NavProgressBar class
27122  * 
27123  * @constructor
27124  * Create a new nav progress bar
27125  * @param {Object} config The config object
27126  */
27127
27128 Roo.bootstrap.NavProgressBar = function(config){
27129     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27130
27131     this.bullets = this.bullets || [];
27132    
27133 //    Roo.bootstrap.NavProgressBar.register(this);
27134      this.addEvents({
27135         /**
27136              * @event changed
27137              * Fires when the active item changes
27138              * @param {Roo.bootstrap.NavProgressBar} this
27139              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27140              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27141          */
27142         'changed': true
27143      });
27144     
27145 };
27146
27147 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27148     
27149     bullets : [],
27150     barItems : [],
27151     
27152     getAutoCreate : function()
27153     {
27154         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27155         
27156         cfg = {
27157             tag : 'div',
27158             cls : 'roo-navigation-bar-group',
27159             cn : [
27160                 {
27161                     tag : 'div',
27162                     cls : 'roo-navigation-top-bar'
27163                 },
27164                 {
27165                     tag : 'div',
27166                     cls : 'roo-navigation-bullets-bar',
27167                     cn : [
27168                         {
27169                             tag : 'ul',
27170                             cls : 'roo-navigation-bar'
27171                         }
27172                     ]
27173                 },
27174                 
27175                 {
27176                     tag : 'div',
27177                     cls : 'roo-navigation-bottom-bar'
27178                 }
27179             ]
27180             
27181         };
27182         
27183         return cfg;
27184         
27185     },
27186     
27187     initEvents: function() 
27188     {
27189         
27190     },
27191     
27192     onRender : function(ct, position) 
27193     {
27194         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27195         
27196         if(this.bullets.length){
27197             Roo.each(this.bullets, function(b){
27198                this.addItem(b);
27199             }, this);
27200         }
27201         
27202         this.format();
27203         
27204     },
27205     
27206     addItem : function(cfg)
27207     {
27208         var item = new Roo.bootstrap.NavProgressItem(cfg);
27209         
27210         item.parentId = this.id;
27211         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27212         
27213         if(cfg.html){
27214             var top = new Roo.bootstrap.Element({
27215                 tag : 'div',
27216                 cls : 'roo-navigation-bar-text'
27217             });
27218             
27219             var bottom = new Roo.bootstrap.Element({
27220                 tag : 'div',
27221                 cls : 'roo-navigation-bar-text'
27222             });
27223             
27224             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27225             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27226             
27227             var topText = new Roo.bootstrap.Element({
27228                 tag : 'span',
27229                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27230             });
27231             
27232             var bottomText = new Roo.bootstrap.Element({
27233                 tag : 'span',
27234                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27235             });
27236             
27237             topText.onRender(top.el, null);
27238             bottomText.onRender(bottom.el, null);
27239             
27240             item.topEl = top;
27241             item.bottomEl = bottom;
27242         }
27243         
27244         this.barItems.push(item);
27245         
27246         return item;
27247     },
27248     
27249     getActive : function()
27250     {
27251         var active = false;
27252         
27253         Roo.each(this.barItems, function(v){
27254             
27255             if (!v.isActive()) {
27256                 return;
27257             }
27258             
27259             active = v;
27260             return false;
27261             
27262         });
27263         
27264         return active;
27265     },
27266     
27267     setActiveItem : function(item)
27268     {
27269         var prev = false;
27270         
27271         Roo.each(this.barItems, function(v){
27272             if (v.rid == item.rid) {
27273                 return ;
27274             }
27275             
27276             if (v.isActive()) {
27277                 v.setActive(false);
27278                 prev = v;
27279             }
27280         });
27281
27282         item.setActive(true);
27283         
27284         this.fireEvent('changed', this, item, prev);
27285     },
27286     
27287     getBarItem: function(rid)
27288     {
27289         var ret = false;
27290         
27291         Roo.each(this.barItems, function(e) {
27292             if (e.rid != rid) {
27293                 return;
27294             }
27295             
27296             ret =  e;
27297             return false;
27298         });
27299         
27300         return ret;
27301     },
27302     
27303     indexOfItem : function(item)
27304     {
27305         var index = false;
27306         
27307         Roo.each(this.barItems, function(v, i){
27308             
27309             if (v.rid != item.rid) {
27310                 return;
27311             }
27312             
27313             index = i;
27314             return false
27315         });
27316         
27317         return index;
27318     },
27319     
27320     setActiveNext : function()
27321     {
27322         var i = this.indexOfItem(this.getActive());
27323         
27324         if (i > this.barItems.length) {
27325             return;
27326         }
27327         
27328         this.setActiveItem(this.barItems[i+1]);
27329     },
27330     
27331     setActivePrev : function()
27332     {
27333         var i = this.indexOfItem(this.getActive());
27334         
27335         if (i  < 1) {
27336             return;
27337         }
27338         
27339         this.setActiveItem(this.barItems[i-1]);
27340     },
27341     
27342     format : function()
27343     {
27344         if(!this.barItems.length){
27345             return;
27346         }
27347      
27348         var width = 100 / this.barItems.length;
27349         
27350         Roo.each(this.barItems, function(i){
27351             i.el.setStyle('width', width + '%');
27352             i.topEl.el.setStyle('width', width + '%');
27353             i.bottomEl.el.setStyle('width', width + '%');
27354         }, this);
27355         
27356     }
27357     
27358 });
27359 /*
27360  * - LGPL
27361  *
27362  * Nav Progress Item
27363  * 
27364  */
27365
27366 /**
27367  * @class Roo.bootstrap.NavProgressItem
27368  * @extends Roo.bootstrap.Component
27369  * Bootstrap NavProgressItem class
27370  * @cfg {String} rid the reference id
27371  * @cfg {Boolean} active (true|false) Is item active default false
27372  * @cfg {Boolean} disabled (true|false) Is item active default false
27373  * @cfg {String} html
27374  * @cfg {String} position (top|bottom) text position default bottom
27375  * @cfg {String} icon show icon instead of number
27376  * 
27377  * @constructor
27378  * Create a new NavProgressItem
27379  * @param {Object} config The config object
27380  */
27381 Roo.bootstrap.NavProgressItem = function(config){
27382     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27383     this.addEvents({
27384         // raw events
27385         /**
27386          * @event click
27387          * The raw click event for the entire grid.
27388          * @param {Roo.bootstrap.NavProgressItem} this
27389          * @param {Roo.EventObject} e
27390          */
27391         "click" : true
27392     });
27393    
27394 };
27395
27396 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27397     
27398     rid : '',
27399     active : false,
27400     disabled : false,
27401     html : '',
27402     position : 'bottom',
27403     icon : false,
27404     
27405     getAutoCreate : function()
27406     {
27407         var iconCls = 'roo-navigation-bar-item-icon';
27408         
27409         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27410         
27411         var cfg = {
27412             tag: 'li',
27413             cls: 'roo-navigation-bar-item',
27414             cn : [
27415                 {
27416                     tag : 'i',
27417                     cls : iconCls
27418                 }
27419             ]
27420         };
27421         
27422         if(this.active){
27423             cfg.cls += ' active';
27424         }
27425         if(this.disabled){
27426             cfg.cls += ' disabled';
27427         }
27428         
27429         return cfg;
27430     },
27431     
27432     disable : function()
27433     {
27434         this.setDisabled(true);
27435     },
27436     
27437     enable : function()
27438     {
27439         this.setDisabled(false);
27440     },
27441     
27442     initEvents: function() 
27443     {
27444         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27445         
27446         this.iconEl.on('click', this.onClick, this);
27447     },
27448     
27449     onClick : function(e)
27450     {
27451         e.preventDefault();
27452         
27453         if(this.disabled){
27454             return;
27455         }
27456         
27457         if(this.fireEvent('click', this, e) === false){
27458             return;
27459         };
27460         
27461         this.parent().setActiveItem(this);
27462     },
27463     
27464     isActive: function () 
27465     {
27466         return this.active;
27467     },
27468     
27469     setActive : function(state)
27470     {
27471         if(this.active == state){
27472             return;
27473         }
27474         
27475         this.active = state;
27476         
27477         if (state) {
27478             this.el.addClass('active');
27479             return;
27480         }
27481         
27482         this.el.removeClass('active');
27483         
27484         return;
27485     },
27486     
27487     setDisabled : function(state)
27488     {
27489         if(this.disabled == state){
27490             return;
27491         }
27492         
27493         this.disabled = state;
27494         
27495         if (state) {
27496             this.el.addClass('disabled');
27497             return;
27498         }
27499         
27500         this.el.removeClass('disabled');
27501     },
27502     
27503     tooltipEl : function()
27504     {
27505         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27506     }
27507 });
27508  
27509
27510  /*
27511  * - LGPL
27512  *
27513  * FieldLabel
27514  * 
27515  */
27516
27517 /**
27518  * @class Roo.bootstrap.FieldLabel
27519  * @extends Roo.bootstrap.Component
27520  * Bootstrap FieldLabel class
27521  * @cfg {String} html contents of the element
27522  * @cfg {String} tag tag of the element default label
27523  * @cfg {String} cls class of the element
27524  * @cfg {String} target label target 
27525  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27526  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27527  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27528  * @cfg {String} iconTooltip default "This field is required"
27529  * 
27530  * @constructor
27531  * Create a new FieldLabel
27532  * @param {Object} config The config object
27533  */
27534
27535 Roo.bootstrap.FieldLabel = function(config){
27536     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27537     
27538     this.addEvents({
27539             /**
27540              * @event invalid
27541              * Fires after the field has been marked as invalid.
27542              * @param {Roo.form.FieldLabel} this
27543              * @param {String} msg The validation message
27544              */
27545             invalid : true,
27546             /**
27547              * @event valid
27548              * Fires after the field has been validated with no errors.
27549              * @param {Roo.form.FieldLabel} this
27550              */
27551             valid : true
27552         });
27553 };
27554
27555 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27556     
27557     tag: 'label',
27558     cls: '',
27559     html: '',
27560     target: '',
27561     allowBlank : true,
27562     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27563     validClass : 'text-success fa fa-lg fa-check',
27564     iconTooltip : 'This field is required',
27565     
27566     getAutoCreate : function(){
27567         
27568         var cfg = {
27569             tag : this.tag,
27570             cls : 'roo-bootstrap-field-label ' + this.cls,
27571             for : this.target,
27572             cn : [
27573                 {
27574                     tag : 'i',
27575                     cls : '',
27576                     tooltip : this.iconTooltip
27577                 },
27578                 {
27579                     tag : 'span',
27580                     html : this.html
27581                 }
27582             ] 
27583         };
27584         
27585         return cfg;
27586     },
27587     
27588     initEvents: function() 
27589     {
27590         Roo.bootstrap.Element.superclass.initEvents.call(this);
27591         
27592         this.iconEl = this.el.select('i', true).first();
27593         
27594         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27595         
27596         Roo.bootstrap.FieldLabel.register(this);
27597     },
27598     
27599     /**
27600      * Mark this field as valid
27601      */
27602     markValid : function()
27603     {
27604         this.iconEl.show();
27605         
27606         this.iconEl.removeClass(this.invalidClass);
27607         
27608         this.iconEl.addClass(this.validClass);
27609         
27610         this.fireEvent('valid', this);
27611     },
27612     
27613     /**
27614      * Mark this field as invalid
27615      * @param {String} msg The validation message
27616      */
27617     markInvalid : function(msg)
27618     {
27619         this.iconEl.show();
27620         
27621         this.iconEl.removeClass(this.validClass);
27622         
27623         this.iconEl.addClass(this.invalidClass);
27624         
27625         this.fireEvent('invalid', this, msg);
27626     }
27627     
27628    
27629 });
27630
27631 Roo.apply(Roo.bootstrap.FieldLabel, {
27632     
27633     groups: {},
27634     
27635      /**
27636     * register a FieldLabel Group
27637     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27638     */
27639     register : function(label)
27640     {
27641         if(this.groups.hasOwnProperty(label.target)){
27642             return;
27643         }
27644      
27645         this.groups[label.target] = label;
27646         
27647     },
27648     /**
27649     * fetch a FieldLabel Group based on the target
27650     * @param {string} target
27651     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27652     */
27653     get: function(target) {
27654         if (typeof(this.groups[target]) == 'undefined') {
27655             return false;
27656         }
27657         
27658         return this.groups[target] ;
27659     }
27660 });
27661
27662  
27663
27664  /*
27665  * - LGPL
27666  *
27667  * page DateSplitField.
27668  * 
27669  */
27670
27671
27672 /**
27673  * @class Roo.bootstrap.DateSplitField
27674  * @extends Roo.bootstrap.Component
27675  * Bootstrap DateSplitField class
27676  * @cfg {string} fieldLabel - the label associated
27677  * @cfg {Number} labelWidth set the width of label (0-12)
27678  * @cfg {String} labelAlign (top|left)
27679  * @cfg {Boolean} dayAllowBlank (true|false) default false
27680  * @cfg {Boolean} monthAllowBlank (true|false) default false
27681  * @cfg {Boolean} yearAllowBlank (true|false) default false
27682  * @cfg {string} dayPlaceholder 
27683  * @cfg {string} monthPlaceholder
27684  * @cfg {string} yearPlaceholder
27685  * @cfg {string} dayFormat default 'd'
27686  * @cfg {string} monthFormat default 'm'
27687  * @cfg {string} yearFormat default 'Y'
27688
27689  *     
27690  * @constructor
27691  * Create a new DateSplitField
27692  * @param {Object} config The config object
27693  */
27694
27695 Roo.bootstrap.DateSplitField = function(config){
27696     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27697     
27698     this.addEvents({
27699         // raw events
27700          /**
27701          * @event years
27702          * getting the data of years
27703          * @param {Roo.bootstrap.DateSplitField} this
27704          * @param {Object} years
27705          */
27706         "years" : true,
27707         /**
27708          * @event days
27709          * getting the data of days
27710          * @param {Roo.bootstrap.DateSplitField} this
27711          * @param {Object} days
27712          */
27713         "days" : true,
27714         /**
27715          * @event invalid
27716          * Fires after the field has been marked as invalid.
27717          * @param {Roo.form.Field} this
27718          * @param {String} msg The validation message
27719          */
27720         invalid : true,
27721        /**
27722          * @event valid
27723          * Fires after the field has been validated with no errors.
27724          * @param {Roo.form.Field} this
27725          */
27726         valid : true
27727     });
27728 };
27729
27730 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
27731     
27732     fieldLabel : '',
27733     labelAlign : 'top',
27734     labelWidth : 3,
27735     dayAllowBlank : false,
27736     monthAllowBlank : false,
27737     yearAllowBlank : false,
27738     dayPlaceholder : '',
27739     monthPlaceholder : '',
27740     yearPlaceholder : '',
27741     dayFormat : 'd',
27742     monthFormat : 'm',
27743     yearFormat : 'Y',
27744     isFormField : true,
27745     
27746     getAutoCreate : function()
27747     {
27748         var cfg = {
27749             tag : 'div',
27750             cls : 'row roo-date-split-field-group',
27751             cn : [
27752                 {
27753                     tag : 'input',
27754                     type : 'hidden',
27755                     cls : 'form-hidden-field roo-date-split-field-group-value',
27756                     name : this.name
27757                 }
27758             ]
27759         };
27760         
27761         if(this.fieldLabel){
27762             cfg.cn.push({
27763                 tag : 'div',
27764                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27765                 cn : [
27766                     {
27767                         tag : 'label',
27768                         html : this.fieldLabel
27769                     }
27770                 ]
27771             });
27772         }
27773         
27774         Roo.each(['day', 'month', 'year'], function(t){
27775             cfg.cn.push({
27776                 tag : 'div',
27777                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27778             });
27779         }, this);
27780         
27781         return cfg;
27782     },
27783     
27784     inputEl: function ()
27785     {
27786         return this.el.select('.roo-date-split-field-group-value', true).first();
27787     },
27788     
27789     onRender : function(ct, position) 
27790     {
27791         var _this = this;
27792         
27793         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27794         
27795         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27796         
27797         this.dayField = new Roo.bootstrap.ComboBox({
27798             allowBlank : this.dayAllowBlank,
27799             alwaysQuery : true,
27800             displayField : 'value',
27801             editable : false,
27802             fieldLabel : '',
27803             forceSelection : true,
27804             mode : 'local',
27805             placeholder : this.dayPlaceholder,
27806             selectOnFocus : true,
27807             tpl : '<div class="select2-result"><b>{value}</b></div>',
27808             triggerAction : 'all',
27809             typeAhead : true,
27810             valueField : 'value',
27811             store : new Roo.data.SimpleStore({
27812                 data : (function() {    
27813                     var days = [];
27814                     _this.fireEvent('days', _this, days);
27815                     return days;
27816                 })(),
27817                 fields : [ 'value' ]
27818             }),
27819             listeners : {
27820                 select : function (_self, record, index)
27821                 {
27822                     _this.setValue(_this.getValue());
27823                 }
27824             }
27825         });
27826
27827         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27828         
27829         this.monthField = new Roo.bootstrap.MonthField({
27830             after : '<i class=\"fa fa-calendar\"></i>',
27831             allowBlank : this.monthAllowBlank,
27832             placeholder : this.monthPlaceholder,
27833             readOnly : true,
27834             listeners : {
27835                 render : function (_self)
27836                 {
27837                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
27838                         e.preventDefault();
27839                         _self.focus();
27840                     });
27841                 },
27842                 select : function (_self, oldvalue, newvalue)
27843                 {
27844                     _this.setValue(_this.getValue());
27845                 }
27846             }
27847         });
27848         
27849         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27850         
27851         this.yearField = new Roo.bootstrap.ComboBox({
27852             allowBlank : this.yearAllowBlank,
27853             alwaysQuery : true,
27854             displayField : 'value',
27855             editable : false,
27856             fieldLabel : '',
27857             forceSelection : true,
27858             mode : 'local',
27859             placeholder : this.yearPlaceholder,
27860             selectOnFocus : true,
27861             tpl : '<div class="select2-result"><b>{value}</b></div>',
27862             triggerAction : 'all',
27863             typeAhead : true,
27864             valueField : 'value',
27865             store : new Roo.data.SimpleStore({
27866                 data : (function() {
27867                     var years = [];
27868                     _this.fireEvent('years', _this, years);
27869                     return years;
27870                 })(),
27871                 fields : [ 'value' ]
27872             }),
27873             listeners : {
27874                 select : function (_self, record, index)
27875                 {
27876                     _this.setValue(_this.getValue());
27877                 }
27878             }
27879         });
27880
27881         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27882     },
27883     
27884     setValue : function(v, format)
27885     {
27886         this.inputEl.dom.value = v;
27887         
27888         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27889         
27890         var d = Date.parseDate(v, f);
27891         
27892         if(!d){
27893             this.validate();
27894             return;
27895         }
27896         
27897         this.setDay(d.format(this.dayFormat));
27898         this.setMonth(d.format(this.monthFormat));
27899         this.setYear(d.format(this.yearFormat));
27900         
27901         this.validate();
27902         
27903         return;
27904     },
27905     
27906     setDay : function(v)
27907     {
27908         this.dayField.setValue(v);
27909         this.inputEl.dom.value = this.getValue();
27910         this.validate();
27911         return;
27912     },
27913     
27914     setMonth : function(v)
27915     {
27916         this.monthField.setValue(v, true);
27917         this.inputEl.dom.value = this.getValue();
27918         this.validate();
27919         return;
27920     },
27921     
27922     setYear : function(v)
27923     {
27924         this.yearField.setValue(v);
27925         this.inputEl.dom.value = this.getValue();
27926         this.validate();
27927         return;
27928     },
27929     
27930     getDay : function()
27931     {
27932         return this.dayField.getValue();
27933     },
27934     
27935     getMonth : function()
27936     {
27937         return this.monthField.getValue();
27938     },
27939     
27940     getYear : function()
27941     {
27942         return this.yearField.getValue();
27943     },
27944     
27945     getValue : function()
27946     {
27947         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27948         
27949         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27950         
27951         return date;
27952     },
27953     
27954     reset : function()
27955     {
27956         this.setDay('');
27957         this.setMonth('');
27958         this.setYear('');
27959         this.inputEl.dom.value = '';
27960         this.validate();
27961         return;
27962     },
27963     
27964     validate : function()
27965     {
27966         var d = this.dayField.validate();
27967         var m = this.monthField.validate();
27968         var y = this.yearField.validate();
27969         
27970         var valid = true;
27971         
27972         if(
27973                 (!this.dayAllowBlank && !d) ||
27974                 (!this.monthAllowBlank && !m) ||
27975                 (!this.yearAllowBlank && !y)
27976         ){
27977             valid = false;
27978         }
27979         
27980         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27981             return valid;
27982         }
27983         
27984         if(valid){
27985             this.markValid();
27986             return valid;
27987         }
27988         
27989         this.markInvalid();
27990         
27991         return valid;
27992     },
27993     
27994     markValid : function()
27995     {
27996         
27997         var label = this.el.select('label', true).first();
27998         var icon = this.el.select('i.fa-star', true).first();
27999
28000         if(label && icon){
28001             icon.remove();
28002         }
28003         
28004         this.fireEvent('valid', this);
28005     },
28006     
28007      /**
28008      * Mark this field as invalid
28009      * @param {String} msg The validation message
28010      */
28011     markInvalid : function(msg)
28012     {
28013         
28014         var label = this.el.select('label', true).first();
28015         var icon = this.el.select('i.fa-star', true).first();
28016
28017         if(label && !icon){
28018             this.el.select('.roo-date-split-field-label', true).createChild({
28019                 tag : 'i',
28020                 cls : 'text-danger fa fa-lg fa-star',
28021                 tooltip : 'This field is required',
28022                 style : 'margin-right:5px;'
28023             }, label, true);
28024         }
28025         
28026         this.fireEvent('invalid', this, msg);
28027     },
28028     
28029     clearInvalid : function()
28030     {
28031         var label = this.el.select('label', true).first();
28032         var icon = this.el.select('i.fa-star', true).first();
28033
28034         if(label && icon){
28035             icon.remove();
28036         }
28037         
28038         this.fireEvent('valid', this);
28039     },
28040     
28041     getName: function()
28042     {
28043         return this.name;
28044     }
28045     
28046 });
28047
28048