docs/default.css
[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 panel  - type - primary/success.....
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         return this.config.length;
5118     },
5119
5120     /**
5121      * Returns the number of columns.
5122      * @return {Number}
5123      */
5124     getColumnCount : function(visibleOnly){
5125         if(visibleOnly === true){
5126             var c = 0;
5127             for(var i = 0, len = this.config.length; i < len; i++){
5128                 if(!this.isHidden(i)){
5129                     c++;
5130                 }
5131             }
5132             return c;
5133         }
5134         return this.config.length;
5135     },
5136
5137     /**
5138      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5139      * @param {Function} fn
5140      * @param {Object} scope (optional)
5141      * @return {Array} result
5142      */
5143     getColumnsBy : function(fn, scope){
5144         var r = [];
5145         for(var i = 0, len = this.config.length; i < len; i++){
5146             var c = this.config[i];
5147             if(fn.call(scope||this, c, i) === true){
5148                 r[r.length] = c;
5149             }
5150         }
5151         return r;
5152     },
5153
5154     /**
5155      * Returns true if the specified column is sortable.
5156      * @param {Number} col The column index
5157      * @return {Boolean}
5158      */
5159     isSortable : function(col){
5160         if(typeof this.config[col].sortable == "undefined"){
5161             return this.defaultSortable;
5162         }
5163         return this.config[col].sortable;
5164     },
5165
5166     /**
5167      * Returns the rendering (formatting) function defined for the column.
5168      * @param {Number} col The column index.
5169      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5170      */
5171     getRenderer : function(col){
5172         if(!this.config[col].renderer){
5173             return Roo.grid.ColumnModel.defaultRenderer;
5174         }
5175         return this.config[col].renderer;
5176     },
5177
5178     /**
5179      * Sets the rendering (formatting) function for a column.
5180      * @param {Number} col The column index
5181      * @param {Function} fn The function to use to process the cell's raw data
5182      * to return HTML markup for the grid view. The render function is called with
5183      * the following parameters:<ul>
5184      * <li>Data value.</li>
5185      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5186      * <li>css A CSS style string to apply to the table cell.</li>
5187      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5188      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5189      * <li>Row index</li>
5190      * <li>Column index</li>
5191      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5192      */
5193     setRenderer : function(col, fn){
5194         this.config[col].renderer = fn;
5195     },
5196
5197     /**
5198      * Returns the width for the specified column.
5199      * @param {Number} col The column index
5200      * @return {Number}
5201      */
5202     getColumnWidth : function(col){
5203         return this.config[col].width * 1 || this.defaultWidth;
5204     },
5205
5206     /**
5207      * Sets the width for a column.
5208      * @param {Number} col The column index
5209      * @param {Number} width The new width
5210      */
5211     setColumnWidth : function(col, width, suppressEvent){
5212         this.config[col].width = width;
5213         this.totalWidth = null;
5214         if(!suppressEvent){
5215              this.fireEvent("widthchange", this, col, width);
5216         }
5217     },
5218
5219     /**
5220      * Returns the total width of all columns.
5221      * @param {Boolean} includeHidden True to include hidden column widths
5222      * @return {Number}
5223      */
5224     getTotalWidth : function(includeHidden){
5225         if(!this.totalWidth){
5226             this.totalWidth = 0;
5227             for(var i = 0, len = this.config.length; i < len; i++){
5228                 if(includeHidden || !this.isHidden(i)){
5229                     this.totalWidth += this.getColumnWidth(i);
5230                 }
5231             }
5232         }
5233         return this.totalWidth;
5234     },
5235
5236     /**
5237      * Returns the header for the specified column.
5238      * @param {Number} col The column index
5239      * @return {String}
5240      */
5241     getColumnHeader : function(col){
5242         return this.config[col].header;
5243     },
5244
5245     /**
5246      * Sets the header for a column.
5247      * @param {Number} col The column index
5248      * @param {String} header The new header
5249      */
5250     setColumnHeader : function(col, header){
5251         this.config[col].header = header;
5252         this.fireEvent("headerchange", this, col, header);
5253     },
5254
5255     /**
5256      * Returns the tooltip for the specified column.
5257      * @param {Number} col The column index
5258      * @return {String}
5259      */
5260     getColumnTooltip : function(col){
5261             return this.config[col].tooltip;
5262     },
5263     /**
5264      * Sets the tooltip for a column.
5265      * @param {Number} col The column index
5266      * @param {String} tooltip The new tooltip
5267      */
5268     setColumnTooltip : function(col, tooltip){
5269             this.config[col].tooltip = tooltip;
5270     },
5271
5272     /**
5273      * Returns the dataIndex for the specified column.
5274      * @param {Number} col The column index
5275      * @return {Number}
5276      */
5277     getDataIndex : function(col){
5278         return this.config[col].dataIndex;
5279     },
5280
5281     /**
5282      * Sets the dataIndex for a column.
5283      * @param {Number} col The column index
5284      * @param {Number} dataIndex The new dataIndex
5285      */
5286     setDataIndex : function(col, dataIndex){
5287         this.config[col].dataIndex = dataIndex;
5288     },
5289
5290     
5291     
5292     /**
5293      * Returns true if the cell is editable.
5294      * @param {Number} colIndex The column index
5295      * @param {Number} rowIndex The row index - this is nto actually used..?
5296      * @return {Boolean}
5297      */
5298     isCellEditable : function(colIndex, rowIndex){
5299         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5300     },
5301
5302     /**
5303      * Returns the editor defined for the cell/column.
5304      * return false or null to disable editing.
5305      * @param {Number} colIndex The column index
5306      * @param {Number} rowIndex The row index
5307      * @return {Object}
5308      */
5309     getCellEditor : function(colIndex, rowIndex){
5310         return this.config[colIndex].editor;
5311     },
5312
5313     /**
5314      * Sets if a column is editable.
5315      * @param {Number} col The column index
5316      * @param {Boolean} editable True if the column is editable
5317      */
5318     setEditable : function(col, editable){
5319         this.config[col].editable = editable;
5320     },
5321
5322
5323     /**
5324      * Returns true if the column is hidden.
5325      * @param {Number} colIndex The column index
5326      * @return {Boolean}
5327      */
5328     isHidden : function(colIndex){
5329         return this.config[colIndex].hidden;
5330     },
5331
5332
5333     /**
5334      * Returns true if the column width cannot be changed
5335      */
5336     isFixed : function(colIndex){
5337         return this.config[colIndex].fixed;
5338     },
5339
5340     /**
5341      * Returns true if the column can be resized
5342      * @return {Boolean}
5343      */
5344     isResizable : function(colIndex){
5345         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5346     },
5347     /**
5348      * Sets if a column is hidden.
5349      * @param {Number} colIndex The column index
5350      * @param {Boolean} hidden True if the column is hidden
5351      */
5352     setHidden : function(colIndex, hidden){
5353         this.config[colIndex].hidden = hidden;
5354         this.totalWidth = null;
5355         this.fireEvent("hiddenchange", this, colIndex, hidden);
5356     },
5357
5358     /**
5359      * Sets the editor for a column.
5360      * @param {Number} col The column index
5361      * @param {Object} editor The editor object
5362      */
5363     setEditor : function(col, editor){
5364         this.config[col].editor = editor;
5365     }
5366 });
5367
5368 Roo.grid.ColumnModel.defaultRenderer = function(value){
5369         if(typeof value == "string" && value.length < 1){
5370             return "&#160;";
5371         }
5372         return value;
5373 };
5374
5375 // Alias for backwards compatibility
5376 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5377 /*
5378  * Based on:
5379  * Ext JS Library 1.1.1
5380  * Copyright(c) 2006-2007, Ext JS, LLC.
5381  *
5382  * Originally Released Under LGPL - original licence link has changed is not relivant.
5383  *
5384  * Fork - LGPL
5385  * <script type="text/javascript">
5386  */
5387  
5388 /**
5389  * @class Roo.LoadMask
5390  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5391  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5392  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5393  * element's UpdateManager load indicator and will be destroyed after the initial load.
5394  * @constructor
5395  * Create a new LoadMask
5396  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5397  * @param {Object} config The config object
5398  */
5399 Roo.LoadMask = function(el, config){
5400     this.el = Roo.get(el);
5401     Roo.apply(this, config);
5402     if(this.store){
5403         this.store.on('beforeload', this.onBeforeLoad, this);
5404         this.store.on('load', this.onLoad, this);
5405         this.store.on('loadexception', this.onLoadException, this);
5406         this.removeMask = false;
5407     }else{
5408         var um = this.el.getUpdateManager();
5409         um.showLoadIndicator = false; // disable the default indicator
5410         um.on('beforeupdate', this.onBeforeLoad, this);
5411         um.on('update', this.onLoad, this);
5412         um.on('failure', this.onLoad, this);
5413         this.removeMask = true;
5414     }
5415 };
5416
5417 Roo.LoadMask.prototype = {
5418     /**
5419      * @cfg {Boolean} removeMask
5420      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5421      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5422      */
5423     /**
5424      * @cfg {String} msg
5425      * The text to display in a centered loading message box (defaults to 'Loading...')
5426      */
5427     msg : 'Loading...',
5428     /**
5429      * @cfg {String} msgCls
5430      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5431      */
5432     msgCls : 'x-mask-loading',
5433
5434     /**
5435      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5436      * @type Boolean
5437      */
5438     disabled: false,
5439
5440     /**
5441      * Disables the mask to prevent it from being displayed
5442      */
5443     disable : function(){
5444        this.disabled = true;
5445     },
5446
5447     /**
5448      * Enables the mask so that it can be displayed
5449      */
5450     enable : function(){
5451         this.disabled = false;
5452     },
5453     
5454     onLoadException : function()
5455     {
5456         Roo.log(arguments);
5457         
5458         if (typeof(arguments[3]) != 'undefined') {
5459             Roo.MessageBox.alert("Error loading",arguments[3]);
5460         } 
5461         /*
5462         try {
5463             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5464                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5465             }   
5466         } catch(e) {
5467             
5468         }
5469         */
5470     
5471         
5472         
5473         this.el.unmask(this.removeMask);
5474     },
5475     // private
5476     onLoad : function()
5477     {
5478         this.el.unmask(this.removeMask);
5479     },
5480
5481     // private
5482     onBeforeLoad : function(){
5483         if(!this.disabled){
5484             this.el.mask(this.msg, this.msgCls);
5485         }
5486     },
5487
5488     // private
5489     destroy : function(){
5490         if(this.store){
5491             this.store.un('beforeload', this.onBeforeLoad, this);
5492             this.store.un('load', this.onLoad, this);
5493             this.store.un('loadexception', this.onLoadException, this);
5494         }else{
5495             var um = this.el.getUpdateManager();
5496             um.un('beforeupdate', this.onBeforeLoad, this);
5497             um.un('update', this.onLoad, this);
5498             um.un('failure', this.onLoad, this);
5499         }
5500     }
5501 };/*
5502  * - LGPL
5503  *
5504  * table
5505  * 
5506  */
5507
5508 /**
5509  * @class Roo.bootstrap.Table
5510  * @extends Roo.bootstrap.Component
5511  * Bootstrap Table class
5512  * @cfg {String} cls table class
5513  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5514  * @cfg {String} bgcolor Specifies the background color for a table
5515  * @cfg {Number} border Specifies whether the table cells should have borders or not
5516  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5517  * @cfg {Number} cellspacing Specifies the space between cells
5518  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5519  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5520  * @cfg {String} sortable Specifies that the table should be sortable
5521  * @cfg {String} summary Specifies a summary of the content of a table
5522  * @cfg {Number} width Specifies the width of a table
5523  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5524  * 
5525  * @cfg {boolean} striped Should the rows be alternative striped
5526  * @cfg {boolean} bordered Add borders to the table
5527  * @cfg {boolean} hover Add hover highlighting
5528  * @cfg {boolean} condensed Format condensed
5529  * @cfg {boolean} responsive Format condensed
5530  * @cfg {Boolean} loadMask (true|false) default false
5531  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5532  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5533  * @cfg {Boolean} rowSelection (true|false) default false
5534  * @cfg {Boolean} cellSelection (true|false) default false
5535  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5536  
5537  * 
5538  * @constructor
5539  * Create a new Table
5540  * @param {Object} config The config object
5541  */
5542
5543 Roo.bootstrap.Table = function(config){
5544     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5545     
5546     // BC...
5547     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5548     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5549     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5550     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5551     
5552     
5553     if (this.sm) {
5554         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5555         this.sm = this.selModel;
5556         this.sm.xmodule = this.xmodule || false;
5557     }
5558     if (this.cm && typeof(this.cm.config) == 'undefined') {
5559         this.colModel = new Roo.grid.ColumnModel(this.cm);
5560         this.cm = this.colModel;
5561         this.cm.xmodule = this.xmodule || false;
5562     }
5563     if (this.store) {
5564         this.store= Roo.factory(this.store, Roo.data);
5565         this.ds = this.store;
5566         this.ds.xmodule = this.xmodule || false;
5567          
5568     }
5569     if (this.footer && this.store) {
5570         this.footer.dataSource = this.ds;
5571         this.footer = Roo.factory(this.footer);
5572     }
5573     
5574     /** @private */
5575     this.addEvents({
5576         /**
5577          * @event cellclick
5578          * Fires when a cell is clicked
5579          * @param {Roo.bootstrap.Table} this
5580          * @param {Roo.Element} el
5581          * @param {Number} rowIndex
5582          * @param {Number} columnIndex
5583          * @param {Roo.EventObject} e
5584          */
5585         "cellclick" : true,
5586         /**
5587          * @event celldblclick
5588          * Fires when a cell is double clicked
5589          * @param {Roo.bootstrap.Table} this
5590          * @param {Roo.Element} el
5591          * @param {Number} rowIndex
5592          * @param {Number} columnIndex
5593          * @param {Roo.EventObject} e
5594          */
5595         "celldblclick" : true,
5596         /**
5597          * @event rowclick
5598          * Fires when a row is clicked
5599          * @param {Roo.bootstrap.Table} this
5600          * @param {Roo.Element} el
5601          * @param {Number} rowIndex
5602          * @param {Roo.EventObject} e
5603          */
5604         "rowclick" : true,
5605         /**
5606          * @event rowdblclick
5607          * Fires when a row is double clicked
5608          * @param {Roo.bootstrap.Table} this
5609          * @param {Roo.Element} el
5610          * @param {Number} rowIndex
5611          * @param {Roo.EventObject} e
5612          */
5613         "rowdblclick" : true,
5614         /**
5615          * @event mouseover
5616          * Fires when a mouseover occur
5617          * @param {Roo.bootstrap.Table} this
5618          * @param {Roo.Element} el
5619          * @param {Number} rowIndex
5620          * @param {Number} columnIndex
5621          * @param {Roo.EventObject} e
5622          */
5623         "mouseover" : true,
5624         /**
5625          * @event mouseout
5626          * Fires when a mouseout occur
5627          * @param {Roo.bootstrap.Table} this
5628          * @param {Roo.Element} el
5629          * @param {Number} rowIndex
5630          * @param {Number} columnIndex
5631          * @param {Roo.EventObject} e
5632          */
5633         "mouseout" : true,
5634         /**
5635          * @event rowclass
5636          * Fires when a row is rendered, so you can change add a style to it.
5637          * @param {Roo.bootstrap.Table} this
5638          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5639          */
5640         'rowclass' : true,
5641           /**
5642          * @event rowsrendered
5643          * Fires when all the  rows have been rendered
5644          * @param {Roo.bootstrap.Table} this
5645          */
5646         'rowsrendered' : true
5647         
5648     });
5649 };
5650
5651 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5652     
5653     cls: false,
5654     align: false,
5655     bgcolor: false,
5656     border: false,
5657     cellpadding: false,
5658     cellspacing: false,
5659     frame: false,
5660     rules: false,
5661     sortable: false,
5662     summary: false,
5663     width: false,
5664     striped : false,
5665     bordered: false,
5666     hover:  false,
5667     condensed : false,
5668     responsive : false,
5669     sm : false,
5670     cm : false,
5671     store : false,
5672     loadMask : false,
5673     footerShow : true,
5674     headerShow : true,
5675   
5676     rowSelection : false,
5677     cellSelection : false,
5678     layout : false,
5679     
5680     // Roo.Element - the tbody
5681     mainBody: false, 
5682     
5683     getAutoCreate : function(){
5684         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5685         
5686         cfg = {
5687             tag: 'table',
5688             cls : 'table',
5689             cn : []
5690         };
5691             
5692         if (this.striped) {
5693             cfg.cls += ' table-striped';
5694         }
5695         
5696         if (this.hover) {
5697             cfg.cls += ' table-hover';
5698         }
5699         if (this.bordered) {
5700             cfg.cls += ' table-bordered';
5701         }
5702         if (this.condensed) {
5703             cfg.cls += ' table-condensed';
5704         }
5705         if (this.responsive) {
5706             cfg.cls += ' table-responsive';
5707         }
5708         
5709         if (this.cls) {
5710             cfg.cls+=  ' ' +this.cls;
5711         }
5712         
5713         // this lot should be simplifed...
5714         
5715         if (this.align) {
5716             cfg.align=this.align;
5717         }
5718         if (this.bgcolor) {
5719             cfg.bgcolor=this.bgcolor;
5720         }
5721         if (this.border) {
5722             cfg.border=this.border;
5723         }
5724         if (this.cellpadding) {
5725             cfg.cellpadding=this.cellpadding;
5726         }
5727         if (this.cellspacing) {
5728             cfg.cellspacing=this.cellspacing;
5729         }
5730         if (this.frame) {
5731             cfg.frame=this.frame;
5732         }
5733         if (this.rules) {
5734             cfg.rules=this.rules;
5735         }
5736         if (this.sortable) {
5737             cfg.sortable=this.sortable;
5738         }
5739         if (this.summary) {
5740             cfg.summary=this.summary;
5741         }
5742         if (this.width) {
5743             cfg.width=this.width;
5744         }
5745         if (this.layout) {
5746             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5747         }
5748         
5749         if(this.store || this.cm){
5750             if(this.headerShow){
5751                 cfg.cn.push(this.renderHeader());
5752             }
5753             
5754             cfg.cn.push(this.renderBody());
5755             
5756             if(this.footerShow){
5757                 cfg.cn.push(this.renderFooter());
5758             }
5759             
5760             cfg.cls+=  ' TableGrid';
5761         }
5762         
5763         return { cn : [ cfg ] };
5764     },
5765     
5766     initEvents : function()
5767     {   
5768         if(!this.store || !this.cm){
5769             return;
5770         }
5771         
5772         //Roo.log('initEvents with ds!!!!');
5773         
5774         this.mainBody = this.el.select('tbody', true).first();
5775         
5776         
5777         var _this = this;
5778         
5779         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5780             e.on('click', _this.sort, _this);
5781         });
5782         
5783         this.el.on("click", this.onClick, this);
5784         this.el.on("dblclick", this.onDblClick, this);
5785         
5786         // why is this done????? = it breaks dialogs??
5787         //this.parent().el.setStyle('position', 'relative');
5788         
5789         
5790         if (this.footer) {
5791             this.footer.parentId = this.id;
5792             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5793         }
5794         
5795         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5796         
5797         this.store.on('load', this.onLoad, this);
5798         this.store.on('beforeload', this.onBeforeLoad, this);
5799         this.store.on('update', this.onUpdate, this);
5800         this.store.on('add', this.onAdd, this);
5801         
5802     },
5803     
5804     onMouseover : function(e, el)
5805     {
5806         var cell = Roo.get(el);
5807         
5808         if(!cell){
5809             return;
5810         }
5811         
5812         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5813             cell = cell.findParent('td', false, true);
5814         }
5815         
5816         var row = cell.findParent('tr', false, true);
5817         var cellIndex = cell.dom.cellIndex;
5818         var rowIndex = row.dom.rowIndex - 1; // start from 0
5819         
5820         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5821         
5822     },
5823     
5824     onMouseout : function(e, el)
5825     {
5826         var cell = Roo.get(el);
5827         
5828         if(!cell){
5829             return;
5830         }
5831         
5832         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5833             cell = cell.findParent('td', false, true);
5834         }
5835         
5836         var row = cell.findParent('tr', false, true);
5837         var cellIndex = cell.dom.cellIndex;
5838         var rowIndex = row.dom.rowIndex - 1; // start from 0
5839         
5840         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5841         
5842     },
5843     
5844     onClick : function(e, el)
5845     {
5846         var cell = Roo.get(el);
5847         
5848         if(!cell || (!this.cellSelection && !this.rowSelection)){
5849             return;
5850         }
5851         
5852         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5853             cell = cell.findParent('td', false, true);
5854         }
5855         
5856         if(!cell || typeof(cell) == 'undefined'){
5857             return;
5858         }
5859         
5860         var row = cell.findParent('tr', false, true);
5861         
5862         if(!row || typeof(row) == 'undefined'){
5863             return;
5864         }
5865         
5866         var cellIndex = cell.dom.cellIndex;
5867         var rowIndex = this.getRowIndex(row);
5868         
5869         // why??? - should these not be based on SelectionModel?
5870         if(this.cellSelection){
5871             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5872         }
5873         
5874         if(this.rowSelection){
5875             this.fireEvent('rowclick', this, row, rowIndex, e);
5876         }
5877         
5878         
5879     },
5880     
5881     onDblClick : function(e,el)
5882     {
5883         var cell = Roo.get(el);
5884         
5885         if(!cell || (!this.CellSelection && !this.RowSelection)){
5886             return;
5887         }
5888         
5889         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5890             cell = cell.findParent('td', false, true);
5891         }
5892         
5893         if(!cell || typeof(cell) == 'undefined'){
5894             return;
5895         }
5896         
5897         var row = cell.findParent('tr', false, true);
5898         
5899         if(!row || typeof(row) == 'undefined'){
5900             return;
5901         }
5902         
5903         var cellIndex = cell.dom.cellIndex;
5904         var rowIndex = this.getRowIndex(row);
5905         
5906         if(this.CellSelection){
5907             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5908         }
5909         
5910         if(this.RowSelection){
5911             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5912         }
5913     },
5914     
5915     sort : function(e,el)
5916     {
5917         var col = Roo.get(el);
5918         
5919         if(!col.hasClass('sortable')){
5920             return;
5921         }
5922         
5923         var sort = col.attr('sort');
5924         var dir = 'ASC';
5925         
5926         if(col.hasClass('glyphicon-arrow-up')){
5927             dir = 'DESC';
5928         }
5929         
5930         this.store.sortInfo = {field : sort, direction : dir};
5931         
5932         if (this.footer) {
5933             Roo.log("calling footer first");
5934             this.footer.onClick('first');
5935         } else {
5936         
5937             this.store.load({ params : { start : 0 } });
5938         }
5939     },
5940     
5941     renderHeader : function()
5942     {
5943         var header = {
5944             tag: 'thead',
5945             cn : []
5946         };
5947         
5948         var cm = this.cm;
5949         
5950         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5951             
5952             var config = cm.config[i];
5953             
5954             var c = {
5955                 tag: 'th',
5956                 style : '',
5957                 html: cm.getColumnHeader(i)
5958             };
5959             
5960             var hh = '';
5961             
5962             if(typeof(config.lgHeader) != 'undefined'){
5963                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5964             }
5965             
5966             if(typeof(config.mdHeader) != 'undefined'){
5967                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5968             }
5969             
5970             if(typeof(config.smHeader) != 'undefined'){
5971                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5972             }
5973             
5974             if(typeof(config.xsHeader) != 'undefined'){
5975                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5976             }
5977             
5978             if(hh.length){
5979                 c.html = hh;
5980             }
5981             
5982             if(typeof(config.tooltip) != 'undefined'){
5983                 c.tooltip = config.tooltip;
5984             }
5985             
5986             if(typeof(config.colspan) != 'undefined'){
5987                 c.colspan = config.colspan;
5988             }
5989             
5990             if(typeof(config.hidden) != 'undefined' && config.hidden){
5991                 c.style += ' display:none;';
5992             }
5993             
5994             if(typeof(config.dataIndex) != 'undefined'){
5995                 c.sort = config.dataIndex;
5996             }
5997             
5998             if(typeof(config.sortable) != 'undefined' && config.sortable){
5999                 c.cls = 'sortable';
6000             }
6001             
6002             if(typeof(config.align) != 'undefined' && config.align.length){
6003                 c.style += ' text-align:' + config.align + ';';
6004             }
6005             
6006             if(typeof(config.width) != 'undefined'){
6007                 c.style += ' width:' + config.width + 'px;';
6008             }
6009             
6010             if(typeof(config.cls) != 'undefined'){
6011                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6012             }
6013             
6014             ['xs','sm','md','lg'].map(function(size){
6015                 
6016                 if(typeof(config[size]) == 'undefined'){
6017                     return;
6018                 }
6019                 
6020                 if (!config[size]) { // 0 = hidden
6021                     c.cls += ' hidden-' + size;
6022                     return;
6023                 }
6024                 
6025                 c.cls += ' col-' + size + '-' + config[size];
6026
6027             });
6028             
6029             header.cn.push(c)
6030         }
6031         
6032         return header;
6033     },
6034     
6035     renderBody : function()
6036     {
6037         var body = {
6038             tag: 'tbody',
6039             cn : [
6040                 {
6041                     tag: 'tr',
6042                     cn : [
6043                         {
6044                             tag : 'td',
6045                             colspan :  this.cm.getColumnCount()
6046                         }
6047                     ]
6048                 }
6049             ]
6050         };
6051         
6052         return body;
6053     },
6054     
6055     renderFooter : function()
6056     {
6057         var footer = {
6058             tag: 'tfoot',
6059             cn : [
6060                 {
6061                     tag: 'tr',
6062                     cn : [
6063                         {
6064                             tag : 'td',
6065                             colspan :  this.cm.getColumnCount()
6066                         }
6067                     ]
6068                 }
6069             ]
6070         };
6071         
6072         return footer;
6073     },
6074     
6075     
6076     
6077     onLoad : function()
6078     {
6079 //        Roo.log('ds onload');
6080         this.clear();
6081         
6082         var _this = this;
6083         var cm = this.cm;
6084         var ds = this.store;
6085         
6086         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6087             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6088             
6089             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6090                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6091             }
6092             
6093             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6094                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6095             }
6096         });
6097         
6098         var tbody =  this.mainBody;
6099               
6100         if(ds.getCount() > 0){
6101             ds.data.each(function(d,rowIndex){
6102                 var row =  this.renderRow(cm, ds, rowIndex);
6103                 
6104                 tbody.createChild(row);
6105                 
6106                 var _this = this;
6107                 
6108                 if(row.cellObjects.length){
6109                     Roo.each(row.cellObjects, function(r){
6110                         _this.renderCellObject(r);
6111                     })
6112                 }
6113                 
6114             }, this);
6115         }
6116         
6117         Roo.each(this.el.select('tbody td', true).elements, function(e){
6118             e.on('mouseover', _this.onMouseover, _this);
6119         });
6120         
6121         Roo.each(this.el.select('tbody td', true).elements, function(e){
6122             e.on('mouseout', _this.onMouseout, _this);
6123         });
6124         this.fireEvent('rowsrendered', this);
6125         //if(this.loadMask){
6126         //    this.maskEl.hide();
6127         //}
6128     },
6129     
6130     
6131     onUpdate : function(ds,record)
6132     {
6133         this.refreshRow(record);
6134     },
6135     
6136     onRemove : function(ds, record, index, isUpdate){
6137         if(isUpdate !== true){
6138             this.fireEvent("beforerowremoved", this, index, record);
6139         }
6140         var bt = this.mainBody.dom;
6141         
6142         var rows = this.el.select('tbody > tr', true).elements;
6143         
6144         if(typeof(rows[index]) != 'undefined'){
6145             bt.removeChild(rows[index].dom);
6146         }
6147         
6148 //        if(bt.rows[index]){
6149 //            bt.removeChild(bt.rows[index]);
6150 //        }
6151         
6152         if(isUpdate !== true){
6153             //this.stripeRows(index);
6154             //this.syncRowHeights(index, index);
6155             //this.layout();
6156             this.fireEvent("rowremoved", this, index, record);
6157         }
6158     },
6159     
6160     onAdd : function(ds, records, rowIndex)
6161     {
6162         //Roo.log('on Add called');
6163         // - note this does not handle multiple adding very well..
6164         var bt = this.mainBody.dom;
6165         for (var i =0 ; i < records.length;i++) {
6166             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6167             //Roo.log(records[i]);
6168             //Roo.log(this.store.getAt(rowIndex+i));
6169             this.insertRow(this.store, rowIndex + i, false);
6170             return;
6171         }
6172         
6173     },
6174     
6175     
6176     refreshRow : function(record){
6177         var ds = this.store, index;
6178         if(typeof record == 'number'){
6179             index = record;
6180             record = ds.getAt(index);
6181         }else{
6182             index = ds.indexOf(record);
6183         }
6184         this.insertRow(ds, index, true);
6185         this.onRemove(ds, record, index+1, true);
6186         //this.syncRowHeights(index, index);
6187         //this.layout();
6188         this.fireEvent("rowupdated", this, index, record);
6189     },
6190     
6191     insertRow : function(dm, rowIndex, isUpdate){
6192         
6193         if(!isUpdate){
6194             this.fireEvent("beforerowsinserted", this, rowIndex);
6195         }
6196             //var s = this.getScrollState();
6197         var row = this.renderRow(this.cm, this.store, rowIndex);
6198         // insert before rowIndex..
6199         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6200         
6201         var _this = this;
6202                 
6203         if(row.cellObjects.length){
6204             Roo.each(row.cellObjects, function(r){
6205                 _this.renderCellObject(r);
6206             })
6207         }
6208             
6209         if(!isUpdate){
6210             this.fireEvent("rowsinserted", this, rowIndex);
6211             //this.syncRowHeights(firstRow, lastRow);
6212             //this.stripeRows(firstRow);
6213             //this.layout();
6214         }
6215         
6216     },
6217     
6218     
6219     getRowDom : function(rowIndex)
6220     {
6221         var rows = this.el.select('tbody > tr', true).elements;
6222         
6223         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6224         
6225     },
6226     // returns the object tree for a tr..
6227   
6228     
6229     renderRow : function(cm, ds, rowIndex) 
6230     {
6231         
6232         var d = ds.getAt(rowIndex);
6233         
6234         var row = {
6235             tag : 'tr',
6236             cn : []
6237         };
6238             
6239         var cellObjects = [];
6240         
6241         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6242             var config = cm.config[i];
6243             
6244             var renderer = cm.getRenderer(i);
6245             var value = '';
6246             var id = false;
6247             
6248             if(typeof(renderer) !== 'undefined'){
6249                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6250             }
6251             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6252             // and are rendered into the cells after the row is rendered - using the id for the element.
6253             
6254             if(typeof(value) === 'object'){
6255                 id = Roo.id();
6256                 cellObjects.push({
6257                     container : id,
6258                     cfg : value 
6259                 })
6260             }
6261             
6262             var rowcfg = {
6263                 record: d,
6264                 rowIndex : rowIndex,
6265                 colIndex : i,
6266                 rowClass : ''
6267             };
6268
6269             this.fireEvent('rowclass', this, rowcfg);
6270             
6271             var td = {
6272                 tag: 'td',
6273                 cls : rowcfg.rowClass,
6274                 style: '',
6275                 html: (typeof(value) === 'object') ? '' : value
6276             };
6277             
6278             if (id) {
6279                 td.id = id;
6280             }
6281             
6282             if(typeof(config.colspan) != 'undefined'){
6283                 td.colspan = config.colspan;
6284             }
6285             
6286             if(typeof(config.hidden) != 'undefined' && config.hidden){
6287                 td.style += ' display:none;';
6288             }
6289             
6290             if(typeof(config.align) != 'undefined' && config.align.length){
6291                 td.style += ' text-align:' + config.align + ';';
6292             }
6293             
6294             if(typeof(config.width) != 'undefined'){
6295                 td.style += ' width:' +  config.width + 'px;';
6296             }
6297             
6298             if(typeof(config.cursor) != 'undefined'){
6299                 td.style += ' cursor:' +  config.cursor + ';';
6300             }
6301             
6302             if(typeof(config.cls) != 'undefined'){
6303                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6304             }
6305             
6306             ['xs','sm','md','lg'].map(function(size){
6307                 
6308                 if(typeof(config[size]) == 'undefined'){
6309                     return;
6310                 }
6311                 
6312                 if (!config[size]) { // 0 = hidden
6313                     td.cls += ' hidden-' + size;
6314                     return;
6315                 }
6316                 
6317                 td.cls += ' col-' + size + '-' + config[size];
6318
6319             });
6320              
6321             row.cn.push(td);
6322            
6323         }
6324         
6325         row.cellObjects = cellObjects;
6326         
6327         return row;
6328           
6329     },
6330     
6331     
6332     
6333     onBeforeLoad : function()
6334     {
6335         //Roo.log('ds onBeforeLoad');
6336         
6337         //this.clear();
6338         
6339         //if(this.loadMask){
6340         //    this.maskEl.show();
6341         //}
6342     },
6343      /**
6344      * Remove all rows
6345      */
6346     clear : function()
6347     {
6348         this.el.select('tbody', true).first().dom.innerHTML = '';
6349     },
6350     /**
6351      * Show or hide a row.
6352      * @param {Number} rowIndex to show or hide
6353      * @param {Boolean} state hide
6354      */
6355     setRowVisibility : function(rowIndex, state)
6356     {
6357         var bt = this.mainBody.dom;
6358         
6359         var rows = this.el.select('tbody > tr', true).elements;
6360         
6361         if(typeof(rows[rowIndex]) == 'undefined'){
6362             return;
6363         }
6364         rows[rowIndex].dom.style.display = state ? '' : 'none';
6365     },
6366     
6367     
6368     getSelectionModel : function(){
6369         if(!this.selModel){
6370             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6371         }
6372         return this.selModel;
6373     },
6374     /*
6375      * Render the Roo.bootstrap object from renderder
6376      */
6377     renderCellObject : function(r)
6378     {
6379         var _this = this;
6380         
6381         var t = r.cfg.render(r.container);
6382         
6383         if(r.cfg.cn){
6384             Roo.each(r.cfg.cn, function(c){
6385                 var child = {
6386                     container: t.getChildContainer(),
6387                     cfg: c
6388                 };
6389                 _this.renderCellObject(child);
6390             })
6391         }
6392     },
6393     
6394     getRowIndex : function(row)
6395     {
6396         var rowIndex = -1;
6397         
6398         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6399             if(el != row){
6400                 return;
6401             }
6402             
6403             rowIndex = index;
6404         });
6405         
6406         return rowIndex;
6407     }
6408    
6409 });
6410
6411  
6412
6413  /*
6414  * - LGPL
6415  *
6416  * table cell
6417  * 
6418  */
6419
6420 /**
6421  * @class Roo.bootstrap.TableCell
6422  * @extends Roo.bootstrap.Component
6423  * Bootstrap TableCell class
6424  * @cfg {String} html cell contain text
6425  * @cfg {String} cls cell class
6426  * @cfg {String} tag cell tag (td|th) default td
6427  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6428  * @cfg {String} align Aligns the content in a cell
6429  * @cfg {String} axis Categorizes cells
6430  * @cfg {String} bgcolor Specifies the background color of a cell
6431  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6432  * @cfg {Number} colspan Specifies the number of columns a cell should span
6433  * @cfg {String} headers Specifies one or more header cells a cell is related to
6434  * @cfg {Number} height Sets the height of a cell
6435  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6436  * @cfg {Number} rowspan Sets the number of rows a cell should span
6437  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6438  * @cfg {String} valign Vertical aligns the content in a cell
6439  * @cfg {Number} width Specifies the width of a cell
6440  * 
6441  * @constructor
6442  * Create a new TableCell
6443  * @param {Object} config The config object
6444  */
6445
6446 Roo.bootstrap.TableCell = function(config){
6447     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6448 };
6449
6450 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6451     
6452     html: false,
6453     cls: false,
6454     tag: false,
6455     abbr: false,
6456     align: false,
6457     axis: false,
6458     bgcolor: false,
6459     charoff: false,
6460     colspan: false,
6461     headers: false,
6462     height: false,
6463     nowrap: false,
6464     rowspan: false,
6465     scope: false,
6466     valign: false,
6467     width: false,
6468     
6469     
6470     getAutoCreate : function(){
6471         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6472         
6473         cfg = {
6474             tag: 'td'
6475         };
6476         
6477         if(this.tag){
6478             cfg.tag = this.tag;
6479         }
6480         
6481         if (this.html) {
6482             cfg.html=this.html
6483         }
6484         if (this.cls) {
6485             cfg.cls=this.cls
6486         }
6487         if (this.abbr) {
6488             cfg.abbr=this.abbr
6489         }
6490         if (this.align) {
6491             cfg.align=this.align
6492         }
6493         if (this.axis) {
6494             cfg.axis=this.axis
6495         }
6496         if (this.bgcolor) {
6497             cfg.bgcolor=this.bgcolor
6498         }
6499         if (this.charoff) {
6500             cfg.charoff=this.charoff
6501         }
6502         if (this.colspan) {
6503             cfg.colspan=this.colspan
6504         }
6505         if (this.headers) {
6506             cfg.headers=this.headers
6507         }
6508         if (this.height) {
6509             cfg.height=this.height
6510         }
6511         if (this.nowrap) {
6512             cfg.nowrap=this.nowrap
6513         }
6514         if (this.rowspan) {
6515             cfg.rowspan=this.rowspan
6516         }
6517         if (this.scope) {
6518             cfg.scope=this.scope
6519         }
6520         if (this.valign) {
6521             cfg.valign=this.valign
6522         }
6523         if (this.width) {
6524             cfg.width=this.width
6525         }
6526         
6527         
6528         return cfg;
6529     }
6530    
6531 });
6532
6533  
6534
6535  /*
6536  * - LGPL
6537  *
6538  * table row
6539  * 
6540  */
6541
6542 /**
6543  * @class Roo.bootstrap.TableRow
6544  * @extends Roo.bootstrap.Component
6545  * Bootstrap TableRow class
6546  * @cfg {String} cls row class
6547  * @cfg {String} align Aligns the content in a table row
6548  * @cfg {String} bgcolor Specifies a background color for a table row
6549  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6550  * @cfg {String} valign Vertical aligns the content in a table row
6551  * 
6552  * @constructor
6553  * Create a new TableRow
6554  * @param {Object} config The config object
6555  */
6556
6557 Roo.bootstrap.TableRow = function(config){
6558     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6559 };
6560
6561 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6562     
6563     cls: false,
6564     align: false,
6565     bgcolor: false,
6566     charoff: false,
6567     valign: false,
6568     
6569     getAutoCreate : function(){
6570         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6571         
6572         cfg = {
6573             tag: 'tr'
6574         };
6575             
6576         if(this.cls){
6577             cfg.cls = this.cls;
6578         }
6579         if(this.align){
6580             cfg.align = this.align;
6581         }
6582         if(this.bgcolor){
6583             cfg.bgcolor = this.bgcolor;
6584         }
6585         if(this.charoff){
6586             cfg.charoff = this.charoff;
6587         }
6588         if(this.valign){
6589             cfg.valign = this.valign;
6590         }
6591         
6592         return cfg;
6593     }
6594    
6595 });
6596
6597  
6598
6599  /*
6600  * - LGPL
6601  *
6602  * table body
6603  * 
6604  */
6605
6606 /**
6607  * @class Roo.bootstrap.TableBody
6608  * @extends Roo.bootstrap.Component
6609  * Bootstrap TableBody class
6610  * @cfg {String} cls element class
6611  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6612  * @cfg {String} align Aligns the content inside the element
6613  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6614  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6615  * 
6616  * @constructor
6617  * Create a new TableBody
6618  * @param {Object} config The config object
6619  */
6620
6621 Roo.bootstrap.TableBody = function(config){
6622     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6623 };
6624
6625 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6626     
6627     cls: false,
6628     tag: false,
6629     align: false,
6630     charoff: false,
6631     valign: false,
6632     
6633     getAutoCreate : function(){
6634         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6635         
6636         cfg = {
6637             tag: 'tbody'
6638         };
6639             
6640         if (this.cls) {
6641             cfg.cls=this.cls
6642         }
6643         if(this.tag){
6644             cfg.tag = this.tag;
6645         }
6646         
6647         if(this.align){
6648             cfg.align = this.align;
6649         }
6650         if(this.charoff){
6651             cfg.charoff = this.charoff;
6652         }
6653         if(this.valign){
6654             cfg.valign = this.valign;
6655         }
6656         
6657         return cfg;
6658     }
6659     
6660     
6661 //    initEvents : function()
6662 //    {
6663 //        
6664 //        if(!this.store){
6665 //            return;
6666 //        }
6667 //        
6668 //        this.store = Roo.factory(this.store, Roo.data);
6669 //        this.store.on('load', this.onLoad, this);
6670 //        
6671 //        this.store.load();
6672 //        
6673 //    },
6674 //    
6675 //    onLoad: function () 
6676 //    {   
6677 //        this.fireEvent('load', this);
6678 //    }
6679 //    
6680 //   
6681 });
6682
6683  
6684
6685  /*
6686  * Based on:
6687  * Ext JS Library 1.1.1
6688  * Copyright(c) 2006-2007, Ext JS, LLC.
6689  *
6690  * Originally Released Under LGPL - original licence link has changed is not relivant.
6691  *
6692  * Fork - LGPL
6693  * <script type="text/javascript">
6694  */
6695
6696 // as we use this in bootstrap.
6697 Roo.namespace('Roo.form');
6698  /**
6699  * @class Roo.form.Action
6700  * Internal Class used to handle form actions
6701  * @constructor
6702  * @param {Roo.form.BasicForm} el The form element or its id
6703  * @param {Object} config Configuration options
6704  */
6705
6706  
6707  
6708 // define the action interface
6709 Roo.form.Action = function(form, options){
6710     this.form = form;
6711     this.options = options || {};
6712 };
6713 /**
6714  * Client Validation Failed
6715  * @const 
6716  */
6717 Roo.form.Action.CLIENT_INVALID = 'client';
6718 /**
6719  * Server Validation Failed
6720  * @const 
6721  */
6722 Roo.form.Action.SERVER_INVALID = 'server';
6723  /**
6724  * Connect to Server Failed
6725  * @const 
6726  */
6727 Roo.form.Action.CONNECT_FAILURE = 'connect';
6728 /**
6729  * Reading Data from Server Failed
6730  * @const 
6731  */
6732 Roo.form.Action.LOAD_FAILURE = 'load';
6733
6734 Roo.form.Action.prototype = {
6735     type : 'default',
6736     failureType : undefined,
6737     response : undefined,
6738     result : undefined,
6739
6740     // interface method
6741     run : function(options){
6742
6743     },
6744
6745     // interface method
6746     success : function(response){
6747
6748     },
6749
6750     // interface method
6751     handleResponse : function(response){
6752
6753     },
6754
6755     // default connection failure
6756     failure : function(response){
6757         
6758         this.response = response;
6759         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6760         this.form.afterAction(this, false);
6761     },
6762
6763     processResponse : function(response){
6764         this.response = response;
6765         if(!response.responseText){
6766             return true;
6767         }
6768         this.result = this.handleResponse(response);
6769         return this.result;
6770     },
6771
6772     // utility functions used internally
6773     getUrl : function(appendParams){
6774         var url = this.options.url || this.form.url || this.form.el.dom.action;
6775         if(appendParams){
6776             var p = this.getParams();
6777             if(p){
6778                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6779             }
6780         }
6781         return url;
6782     },
6783
6784     getMethod : function(){
6785         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6786     },
6787
6788     getParams : function(){
6789         var bp = this.form.baseParams;
6790         var p = this.options.params;
6791         if(p){
6792             if(typeof p == "object"){
6793                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6794             }else if(typeof p == 'string' && bp){
6795                 p += '&' + Roo.urlEncode(bp);
6796             }
6797         }else if(bp){
6798             p = Roo.urlEncode(bp);
6799         }
6800         return p;
6801     },
6802
6803     createCallback : function(){
6804         return {
6805             success: this.success,
6806             failure: this.failure,
6807             scope: this,
6808             timeout: (this.form.timeout*1000),
6809             upload: this.form.fileUpload ? this.success : undefined
6810         };
6811     }
6812 };
6813
6814 Roo.form.Action.Submit = function(form, options){
6815     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6816 };
6817
6818 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6819     type : 'submit',
6820
6821     haveProgress : false,
6822     uploadComplete : false,
6823     
6824     // uploadProgress indicator.
6825     uploadProgress : function()
6826     {
6827         if (!this.form.progressUrl) {
6828             return;
6829         }
6830         
6831         if (!this.haveProgress) {
6832             Roo.MessageBox.progress("Uploading", "Uploading");
6833         }
6834         if (this.uploadComplete) {
6835            Roo.MessageBox.hide();
6836            return;
6837         }
6838         
6839         this.haveProgress = true;
6840    
6841         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6842         
6843         var c = new Roo.data.Connection();
6844         c.request({
6845             url : this.form.progressUrl,
6846             params: {
6847                 id : uid
6848             },
6849             method: 'GET',
6850             success : function(req){
6851                //console.log(data);
6852                 var rdata = false;
6853                 var edata;
6854                 try  {
6855                    rdata = Roo.decode(req.responseText)
6856                 } catch (e) {
6857                     Roo.log("Invalid data from server..");
6858                     Roo.log(edata);
6859                     return;
6860                 }
6861                 if (!rdata || !rdata.success) {
6862                     Roo.log(rdata);
6863                     Roo.MessageBox.alert(Roo.encode(rdata));
6864                     return;
6865                 }
6866                 var data = rdata.data;
6867                 
6868                 if (this.uploadComplete) {
6869                    Roo.MessageBox.hide();
6870                    return;
6871                 }
6872                    
6873                 if (data){
6874                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6875                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6876                     );
6877                 }
6878                 this.uploadProgress.defer(2000,this);
6879             },
6880        
6881             failure: function(data) {
6882                 Roo.log('progress url failed ');
6883                 Roo.log(data);
6884             },
6885             scope : this
6886         });
6887            
6888     },
6889     
6890     
6891     run : function()
6892     {
6893         // run get Values on the form, so it syncs any secondary forms.
6894         this.form.getValues();
6895         
6896         var o = this.options;
6897         var method = this.getMethod();
6898         var isPost = method == 'POST';
6899         if(o.clientValidation === false || this.form.isValid()){
6900             
6901             if (this.form.progressUrl) {
6902                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6903                     (new Date() * 1) + '' + Math.random());
6904                     
6905             } 
6906             
6907             
6908             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6909                 form:this.form.el.dom,
6910                 url:this.getUrl(!isPost),
6911                 method: method,
6912                 params:isPost ? this.getParams() : null,
6913                 isUpload: this.form.fileUpload
6914             }));
6915             
6916             this.uploadProgress();
6917
6918         }else if (o.clientValidation !== false){ // client validation failed
6919             this.failureType = Roo.form.Action.CLIENT_INVALID;
6920             this.form.afterAction(this, false);
6921         }
6922     },
6923
6924     success : function(response)
6925     {
6926         this.uploadComplete= true;
6927         if (this.haveProgress) {
6928             Roo.MessageBox.hide();
6929         }
6930         
6931         
6932         var result = this.processResponse(response);
6933         if(result === true || result.success){
6934             this.form.afterAction(this, true);
6935             return;
6936         }
6937         if(result.errors){
6938             this.form.markInvalid(result.errors);
6939             this.failureType = Roo.form.Action.SERVER_INVALID;
6940         }
6941         this.form.afterAction(this, false);
6942     },
6943     failure : function(response)
6944     {
6945         this.uploadComplete= true;
6946         if (this.haveProgress) {
6947             Roo.MessageBox.hide();
6948         }
6949         
6950         this.response = response;
6951         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6952         this.form.afterAction(this, false);
6953     },
6954     
6955     handleResponse : function(response){
6956         if(this.form.errorReader){
6957             var rs = this.form.errorReader.read(response);
6958             var errors = [];
6959             if(rs.records){
6960                 for(var i = 0, len = rs.records.length; i < len; i++) {
6961                     var r = rs.records[i];
6962                     errors[i] = r.data;
6963                 }
6964             }
6965             if(errors.length < 1){
6966                 errors = null;
6967             }
6968             return {
6969                 success : rs.success,
6970                 errors : errors
6971             };
6972         }
6973         var ret = false;
6974         try {
6975             ret = Roo.decode(response.responseText);
6976         } catch (e) {
6977             ret = {
6978                 success: false,
6979                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6980                 errors : []
6981             };
6982         }
6983         return ret;
6984         
6985     }
6986 });
6987
6988
6989 Roo.form.Action.Load = function(form, options){
6990     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6991     this.reader = this.form.reader;
6992 };
6993
6994 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6995     type : 'load',
6996
6997     run : function(){
6998         
6999         Roo.Ajax.request(Roo.apply(
7000                 this.createCallback(), {
7001                     method:this.getMethod(),
7002                     url:this.getUrl(false),
7003                     params:this.getParams()
7004         }));
7005     },
7006
7007     success : function(response){
7008         
7009         var result = this.processResponse(response);
7010         if(result === true || !result.success || !result.data){
7011             this.failureType = Roo.form.Action.LOAD_FAILURE;
7012             this.form.afterAction(this, false);
7013             return;
7014         }
7015         this.form.clearInvalid();
7016         this.form.setValues(result.data);
7017         this.form.afterAction(this, true);
7018     },
7019
7020     handleResponse : function(response){
7021         if(this.form.reader){
7022             var rs = this.form.reader.read(response);
7023             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7024             return {
7025                 success : rs.success,
7026                 data : data
7027             };
7028         }
7029         return Roo.decode(response.responseText);
7030     }
7031 });
7032
7033 Roo.form.Action.ACTION_TYPES = {
7034     'load' : Roo.form.Action.Load,
7035     'submit' : Roo.form.Action.Submit
7036 };/*
7037  * - LGPL
7038  *
7039  * form
7040  * 
7041  */
7042
7043 /**
7044  * @class Roo.bootstrap.Form
7045  * @extends Roo.bootstrap.Component
7046  * Bootstrap Form class
7047  * @cfg {String} method  GET | POST (default POST)
7048  * @cfg {String} labelAlign top | left (default top)
7049  * @cfg {String} align left  | right - for navbars
7050  * @cfg {Boolean} loadMask load mask when submit (default true)
7051
7052  * 
7053  * @constructor
7054  * Create a new Form
7055  * @param {Object} config The config object
7056  */
7057
7058
7059 Roo.bootstrap.Form = function(config){
7060     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7061     this.addEvents({
7062         /**
7063          * @event clientvalidation
7064          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7065          * @param {Form} this
7066          * @param {Boolean} valid true if the form has passed client-side validation
7067          */
7068         clientvalidation: true,
7069         /**
7070          * @event beforeaction
7071          * Fires before any action is performed. Return false to cancel the action.
7072          * @param {Form} this
7073          * @param {Action} action The action to be performed
7074          */
7075         beforeaction: true,
7076         /**
7077          * @event actionfailed
7078          * Fires when an action fails.
7079          * @param {Form} this
7080          * @param {Action} action The action that failed
7081          */
7082         actionfailed : true,
7083         /**
7084          * @event actioncomplete
7085          * Fires when an action is completed.
7086          * @param {Form} this
7087          * @param {Action} action The action that completed
7088          */
7089         actioncomplete : true
7090     });
7091     
7092 };
7093
7094 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7095       
7096      /**
7097      * @cfg {String} method
7098      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7099      */
7100     method : 'POST',
7101     /**
7102      * @cfg {String} url
7103      * The URL to use for form actions if one isn't supplied in the action options.
7104      */
7105     /**
7106      * @cfg {Boolean} fileUpload
7107      * Set to true if this form is a file upload.
7108      */
7109      
7110     /**
7111      * @cfg {Object} baseParams
7112      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7113      */
7114       
7115     /**
7116      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7117      */
7118     timeout: 30,
7119     /**
7120      * @cfg {Sting} align (left|right) for navbar forms
7121      */
7122     align : 'left',
7123
7124     // private
7125     activeAction : null,
7126  
7127     /**
7128      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7129      * element by passing it or its id or mask the form itself by passing in true.
7130      * @type Mixed
7131      */
7132     waitMsgTarget : false,
7133     
7134     loadMask : true,
7135     
7136     getAutoCreate : function(){
7137         
7138         var cfg = {
7139             tag: 'form',
7140             method : this.method || 'POST',
7141             id : this.id || Roo.id(),
7142             cls : ''
7143         };
7144         if (this.parent().xtype.match(/^Nav/)) {
7145             cfg.cls = 'navbar-form navbar-' + this.align;
7146             
7147         }
7148         
7149         if (this.labelAlign == 'left' ) {
7150             cfg.cls += ' form-horizontal';
7151         }
7152         
7153         
7154         return cfg;
7155     },
7156     initEvents : function()
7157     {
7158         this.el.on('submit', this.onSubmit, this);
7159         // this was added as random key presses on the form where triggering form submit.
7160         this.el.on('keypress', function(e) {
7161             if (e.getCharCode() != 13) {
7162                 return true;
7163             }
7164             // we might need to allow it for textareas.. and some other items.
7165             // check e.getTarget().
7166             
7167             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7168                 return true;
7169             }
7170         
7171             Roo.log("keypress blocked");
7172             
7173             e.preventDefault();
7174             return false;
7175         });
7176         
7177     },
7178     // private
7179     onSubmit : function(e){
7180         e.stopEvent();
7181     },
7182     
7183      /**
7184      * Returns true if client-side validation on the form is successful.
7185      * @return Boolean
7186      */
7187     isValid : function(){
7188         var items = this.getItems();
7189         var valid = true;
7190         items.each(function(f){
7191            if(!f.validate()){
7192                valid = false;
7193                
7194            }
7195         });
7196         return valid;
7197     },
7198     /**
7199      * Returns true if any fields in this form have changed since their original load.
7200      * @return Boolean
7201      */
7202     isDirty : function(){
7203         var dirty = false;
7204         var items = this.getItems();
7205         items.each(function(f){
7206            if(f.isDirty()){
7207                dirty = true;
7208                return false;
7209            }
7210            return true;
7211         });
7212         return dirty;
7213     },
7214      /**
7215      * Performs a predefined action (submit or load) or custom actions you define on this form.
7216      * @param {String} actionName The name of the action type
7217      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7218      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7219      * accept other config options):
7220      * <pre>
7221 Property          Type             Description
7222 ----------------  ---------------  ----------------------------------------------------------------------------------
7223 url               String           The url for the action (defaults to the form's url)
7224 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7225 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7226 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7227                                    validate the form on the client (defaults to false)
7228      * </pre>
7229      * @return {BasicForm} this
7230      */
7231     doAction : function(action, options){
7232         if(typeof action == 'string'){
7233             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7234         }
7235         if(this.fireEvent('beforeaction', this, action) !== false){
7236             this.beforeAction(action);
7237             action.run.defer(100, action);
7238         }
7239         return this;
7240     },
7241     
7242     // private
7243     beforeAction : function(action){
7244         var o = action.options;
7245         
7246         if(this.loadMask){
7247             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7248         }
7249         // not really supported yet.. ??
7250         
7251         //if(this.waitMsgTarget === true){
7252         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7253         //}else if(this.waitMsgTarget){
7254         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7255         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7256         //}else {
7257         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7258        // }
7259          
7260     },
7261
7262     // private
7263     afterAction : function(action, success){
7264         this.activeAction = null;
7265         var o = action.options;
7266         
7267         //if(this.waitMsgTarget === true){
7268             this.el.unmask();
7269         //}else if(this.waitMsgTarget){
7270         //    this.waitMsgTarget.unmask();
7271         //}else{
7272         //    Roo.MessageBox.updateProgress(1);
7273         //    Roo.MessageBox.hide();
7274        // }
7275         // 
7276         if(success){
7277             if(o.reset){
7278                 this.reset();
7279             }
7280             Roo.callback(o.success, o.scope, [this, action]);
7281             this.fireEvent('actioncomplete', this, action);
7282             
7283         }else{
7284             
7285             // failure condition..
7286             // we have a scenario where updates need confirming.
7287             // eg. if a locking scenario exists..
7288             // we look for { errors : { needs_confirm : true }} in the response.
7289             if (
7290                 (typeof(action.result) != 'undefined')  &&
7291                 (typeof(action.result.errors) != 'undefined')  &&
7292                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7293            ){
7294                 var _t = this;
7295                 Roo.log("not supported yet");
7296                  /*
7297                 
7298                 Roo.MessageBox.confirm(
7299                     "Change requires confirmation",
7300                     action.result.errorMsg,
7301                     function(r) {
7302                         if (r != 'yes') {
7303                             return;
7304                         }
7305                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7306                     }
7307                     
7308                 );
7309                 */
7310                 
7311                 
7312                 return;
7313             }
7314             
7315             Roo.callback(o.failure, o.scope, [this, action]);
7316             // show an error message if no failed handler is set..
7317             if (!this.hasListener('actionfailed')) {
7318                 Roo.log("need to add dialog support");
7319                 /*
7320                 Roo.MessageBox.alert("Error",
7321                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7322                         action.result.errorMsg :
7323                         "Saving Failed, please check your entries or try again"
7324                 );
7325                 */
7326             }
7327             
7328             this.fireEvent('actionfailed', this, action);
7329         }
7330         
7331     },
7332     /**
7333      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7334      * @param {String} id The value to search for
7335      * @return Field
7336      */
7337     findField : function(id){
7338         var items = this.getItems();
7339         var field = items.get(id);
7340         if(!field){
7341              items.each(function(f){
7342                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7343                     field = f;
7344                     return false;
7345                 }
7346                 return true;
7347             });
7348         }
7349         return field || null;
7350     },
7351      /**
7352      * Mark fields in this form invalid in bulk.
7353      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7354      * @return {BasicForm} this
7355      */
7356     markInvalid : function(errors){
7357         if(errors instanceof Array){
7358             for(var i = 0, len = errors.length; i < len; i++){
7359                 var fieldError = errors[i];
7360                 var f = this.findField(fieldError.id);
7361                 if(f){
7362                     f.markInvalid(fieldError.msg);
7363                 }
7364             }
7365         }else{
7366             var field, id;
7367             for(id in errors){
7368                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7369                     field.markInvalid(errors[id]);
7370                 }
7371             }
7372         }
7373         //Roo.each(this.childForms || [], function (f) {
7374         //    f.markInvalid(errors);
7375         //});
7376         
7377         return this;
7378     },
7379
7380     /**
7381      * Set values for fields in this form in bulk.
7382      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7383      * @return {BasicForm} this
7384      */
7385     setValues : function(values){
7386         if(values instanceof Array){ // array of objects
7387             for(var i = 0, len = values.length; i < len; i++){
7388                 var v = values[i];
7389                 var f = this.findField(v.id);
7390                 if(f){
7391                     f.setValue(v.value);
7392                     if(this.trackResetOnLoad){
7393                         f.originalValue = f.getValue();
7394                     }
7395                 }
7396             }
7397         }else{ // object hash
7398             var field, id;
7399             for(id in values){
7400                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7401                     
7402                     if (field.setFromData && 
7403                         field.valueField && 
7404                         field.displayField &&
7405                         // combos' with local stores can 
7406                         // be queried via setValue()
7407                         // to set their value..
7408                         (field.store && !field.store.isLocal)
7409                         ) {
7410                         // it's a combo
7411                         var sd = { };
7412                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7413                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7414                         field.setFromData(sd);
7415                         
7416                     } else {
7417                         field.setValue(values[id]);
7418                     }
7419                     
7420                     
7421                     if(this.trackResetOnLoad){
7422                         field.originalValue = field.getValue();
7423                     }
7424                 }
7425             }
7426         }
7427          
7428         //Roo.each(this.childForms || [], function (f) {
7429         //    f.setValues(values);
7430         //});
7431                 
7432         return this;
7433     },
7434
7435     /**
7436      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7437      * they are returned as an array.
7438      * @param {Boolean} asString
7439      * @return {Object}
7440      */
7441     getValues : function(asString){
7442         //if (this.childForms) {
7443             // copy values from the child forms
7444         //    Roo.each(this.childForms, function (f) {
7445         //        this.setValues(f.getValues());
7446         //    }, this);
7447         //}
7448         
7449         
7450         
7451         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7452         if(asString === true){
7453             return fs;
7454         }
7455         return Roo.urlDecode(fs);
7456     },
7457     
7458     /**
7459      * Returns the fields in this form as an object with key/value pairs. 
7460      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7461      * @return {Object}
7462      */
7463     getFieldValues : function(with_hidden)
7464     {
7465         var items = this.getItems();
7466         var ret = {};
7467         items.each(function(f){
7468             if (!f.getName()) {
7469                 return;
7470             }
7471             var v = f.getValue();
7472             if (f.inputType =='radio') {
7473                 if (typeof(ret[f.getName()]) == 'undefined') {
7474                     ret[f.getName()] = ''; // empty..
7475                 }
7476                 
7477                 if (!f.el.dom.checked) {
7478                     return;
7479                     
7480                 }
7481                 v = f.el.dom.value;
7482                 
7483             }
7484             
7485             // not sure if this supported any more..
7486             if ((typeof(v) == 'object') && f.getRawValue) {
7487                 v = f.getRawValue() ; // dates..
7488             }
7489             // combo boxes where name != hiddenName...
7490             if (f.name != f.getName()) {
7491                 ret[f.name] = f.getRawValue();
7492             }
7493             ret[f.getName()] = v;
7494         });
7495         
7496         return ret;
7497     },
7498
7499     /**
7500      * Clears all invalid messages in this form.
7501      * @return {BasicForm} this
7502      */
7503     clearInvalid : function(){
7504         var items = this.getItems();
7505         
7506         items.each(function(f){
7507            f.clearInvalid();
7508         });
7509         
7510         
7511         
7512         return this;
7513     },
7514
7515     /**
7516      * Resets this form.
7517      * @return {BasicForm} this
7518      */
7519     reset : function(){
7520         var items = this.getItems();
7521         items.each(function(f){
7522             f.reset();
7523         });
7524         
7525         Roo.each(this.childForms || [], function (f) {
7526             f.reset();
7527         });
7528        
7529         
7530         return this;
7531     },
7532     getItems : function()
7533     {
7534         var r=new Roo.util.MixedCollection(false, function(o){
7535             return o.id || (o.id = Roo.id());
7536         });
7537         var iter = function(el) {
7538             if (el.inputEl) {
7539                 r.add(el);
7540             }
7541             if (!el.items) {
7542                 return;
7543             }
7544             Roo.each(el.items,function(e) {
7545                 iter(e);
7546             });
7547             
7548             
7549         };
7550         
7551         iter(this);
7552         return r;
7553         
7554         
7555         
7556         
7557     }
7558     
7559 });
7560
7561  
7562 /*
7563  * Based on:
7564  * Ext JS Library 1.1.1
7565  * Copyright(c) 2006-2007, Ext JS, LLC.
7566  *
7567  * Originally Released Under LGPL - original licence link has changed is not relivant.
7568  *
7569  * Fork - LGPL
7570  * <script type="text/javascript">
7571  */
7572 /**
7573  * @class Roo.form.VTypes
7574  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7575  * @singleton
7576  */
7577 Roo.form.VTypes = function(){
7578     // closure these in so they are only created once.
7579     var alpha = /^[a-zA-Z_]+$/;
7580     var alphanum = /^[a-zA-Z0-9_]+$/;
7581     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7582     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7583
7584     // All these messages and functions are configurable
7585     return {
7586         /**
7587          * The function used to validate email addresses
7588          * @param {String} value The email address
7589          */
7590         'email' : function(v){
7591             return email.test(v);
7592         },
7593         /**
7594          * The error text to display when the email validation function returns false
7595          * @type String
7596          */
7597         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7598         /**
7599          * The keystroke filter mask to be applied on email input
7600          * @type RegExp
7601          */
7602         'emailMask' : /[a-z0-9_\.\-@]/i,
7603
7604         /**
7605          * The function used to validate URLs
7606          * @param {String} value The URL
7607          */
7608         'url' : function(v){
7609             return url.test(v);
7610         },
7611         /**
7612          * The error text to display when the url validation function returns false
7613          * @type String
7614          */
7615         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7616         
7617         /**
7618          * The function used to validate alpha values
7619          * @param {String} value The value
7620          */
7621         'alpha' : function(v){
7622             return alpha.test(v);
7623         },
7624         /**
7625          * The error text to display when the alpha validation function returns false
7626          * @type String
7627          */
7628         'alphaText' : 'This field should only contain letters and _',
7629         /**
7630          * The keystroke filter mask to be applied on alpha input
7631          * @type RegExp
7632          */
7633         'alphaMask' : /[a-z_]/i,
7634
7635         /**
7636          * The function used to validate alphanumeric values
7637          * @param {String} value The value
7638          */
7639         'alphanum' : function(v){
7640             return alphanum.test(v);
7641         },
7642         /**
7643          * The error text to display when the alphanumeric validation function returns false
7644          * @type String
7645          */
7646         'alphanumText' : 'This field should only contain letters, numbers and _',
7647         /**
7648          * The keystroke filter mask to be applied on alphanumeric input
7649          * @type RegExp
7650          */
7651         'alphanumMask' : /[a-z0-9_]/i
7652     };
7653 }();/*
7654  * - LGPL
7655  *
7656  * Input
7657  * 
7658  */
7659
7660 /**
7661  * @class Roo.bootstrap.Input
7662  * @extends Roo.bootstrap.Component
7663  * Bootstrap Input class
7664  * @cfg {Boolean} disabled is it disabled
7665  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7666  * @cfg {String} name name of the input
7667  * @cfg {string} fieldLabel - the label associated
7668  * @cfg {string} placeholder - placeholder to put in text.
7669  * @cfg {string}  before - input group add on before
7670  * @cfg {string} after - input group add on after
7671  * @cfg {string} size - (lg|sm) or leave empty..
7672  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7673  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7674  * @cfg {Number} md colspan out of 12 for computer-sized screens
7675  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7676  * @cfg {string} value default value of the input
7677  * @cfg {Number} labelWidth set the width of label (0-12)
7678  * @cfg {String} labelAlign (top|left)
7679  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7680  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7681
7682  * @cfg {String} align (left|center|right) Default left
7683  * @cfg {Boolean} forceFeedback (true|false) Default false
7684  * 
7685  * 
7686  * 
7687  * 
7688  * @constructor
7689  * Create a new Input
7690  * @param {Object} config The config object
7691  */
7692
7693 Roo.bootstrap.Input = function(config){
7694     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7695    
7696         this.addEvents({
7697             /**
7698              * @event focus
7699              * Fires when this field receives input focus.
7700              * @param {Roo.form.Field} this
7701              */
7702             focus : true,
7703             /**
7704              * @event blur
7705              * Fires when this field loses input focus.
7706              * @param {Roo.form.Field} this
7707              */
7708             blur : true,
7709             /**
7710              * @event specialkey
7711              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7712              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7713              * @param {Roo.form.Field} this
7714              * @param {Roo.EventObject} e The event object
7715              */
7716             specialkey : true,
7717             /**
7718              * @event change
7719              * Fires just before the field blurs if the field value has changed.
7720              * @param {Roo.form.Field} this
7721              * @param {Mixed} newValue The new value
7722              * @param {Mixed} oldValue The original value
7723              */
7724             change : true,
7725             /**
7726              * @event invalid
7727              * Fires after the field has been marked as invalid.
7728              * @param {Roo.form.Field} this
7729              * @param {String} msg The validation message
7730              */
7731             invalid : true,
7732             /**
7733              * @event valid
7734              * Fires after the field has been validated with no errors.
7735              * @param {Roo.form.Field} this
7736              */
7737             valid : true,
7738              /**
7739              * @event keyup
7740              * Fires after the key up
7741              * @param {Roo.form.Field} this
7742              * @param {Roo.EventObject}  e The event Object
7743              */
7744             keyup : true
7745         });
7746 };
7747
7748 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7749      /**
7750      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7751       automatic validation (defaults to "keyup").
7752      */
7753     validationEvent : "keyup",
7754      /**
7755      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7756      */
7757     validateOnBlur : true,
7758     /**
7759      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7760      */
7761     validationDelay : 250,
7762      /**
7763      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7764      */
7765     focusClass : "x-form-focus",  // not needed???
7766     
7767        
7768     /**
7769      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7770      */
7771     invalidClass : "has-warning",
7772     
7773     /**
7774      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7775      */
7776     validClass : "has-success",
7777     
7778     /**
7779      * @cfg {Boolean} hasFeedback (true|false) default true
7780      */
7781     hasFeedback : true,
7782     
7783     /**
7784      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7785      */
7786     invalidFeedbackClass : "glyphicon-warning-sign",
7787     
7788     /**
7789      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7790      */
7791     validFeedbackClass : "glyphicon-ok",
7792     
7793     /**
7794      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7795      */
7796     selectOnFocus : false,
7797     
7798      /**
7799      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7800      */
7801     maskRe : null,
7802        /**
7803      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7804      */
7805     vtype : null,
7806     
7807       /**
7808      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7809      */
7810     disableKeyFilter : false,
7811     
7812        /**
7813      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7814      */
7815     disabled : false,
7816      /**
7817      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7818      */
7819     allowBlank : true,
7820     /**
7821      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7822      */
7823     blankText : "This field is required",
7824     
7825      /**
7826      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7827      */
7828     minLength : 0,
7829     /**
7830      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7831      */
7832     maxLength : Number.MAX_VALUE,
7833     /**
7834      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7835      */
7836     minLengthText : "The minimum length for this field is {0}",
7837     /**
7838      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7839      */
7840     maxLengthText : "The maximum length for this field is {0}",
7841   
7842     
7843     /**
7844      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7845      * If available, this function will be called only after the basic validators all return true, and will be passed the
7846      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7847      */
7848     validator : null,
7849     /**
7850      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7851      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7852      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7853      */
7854     regex : null,
7855     /**
7856      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7857      */
7858     regexText : "",
7859     
7860     autocomplete: false,
7861     
7862     
7863     fieldLabel : '',
7864     inputType : 'text',
7865     
7866     name : false,
7867     placeholder: false,
7868     before : false,
7869     after : false,
7870     size : false,
7871     hasFocus : false,
7872     preventMark: false,
7873     isFormField : true,
7874     value : '',
7875     labelWidth : 2,
7876     labelAlign : false,
7877     readOnly : false,
7878     align : false,
7879     formatedValue : false,
7880     forceFeedback : false,
7881     
7882     parentLabelAlign : function()
7883     {
7884         var parent = this;
7885         while (parent.parent()) {
7886             parent = parent.parent();
7887             if (typeof(parent.labelAlign) !='undefined') {
7888                 return parent.labelAlign;
7889             }
7890         }
7891         return 'left';
7892         
7893     },
7894     
7895     getAutoCreate : function(){
7896         
7897         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7898         
7899         var id = Roo.id();
7900         
7901         var cfg = {};
7902         
7903         if(this.inputType != 'hidden'){
7904             cfg.cls = 'form-group' //input-group
7905         }
7906         
7907         var input =  {
7908             tag: 'input',
7909             id : id,
7910             type : this.inputType,
7911             value : this.value,
7912             cls : 'form-control',
7913             placeholder : this.placeholder || '',
7914             autocomplete : this.autocomplete || 'new-password'
7915         };
7916         
7917         
7918         if(this.align){
7919             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7920         }
7921         
7922         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7923             input.maxLength = this.maxLength;
7924         }
7925         
7926         if (this.disabled) {
7927             input.disabled=true;
7928         }
7929         
7930         if (this.readOnly) {
7931             input.readonly=true;
7932         }
7933         
7934         if (this.name) {
7935             input.name = this.name;
7936         }
7937         if (this.size) {
7938             input.cls += ' input-' + this.size;
7939         }
7940         var settings=this;
7941         ['xs','sm','md','lg'].map(function(size){
7942             if (settings[size]) {
7943                 cfg.cls += ' col-' + size + '-' + settings[size];
7944             }
7945         });
7946         
7947         var inputblock = input;
7948         
7949         var feedback = {
7950             tag: 'span',
7951             cls: 'glyphicon form-control-feedback'
7952         };
7953             
7954         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7955             
7956             inputblock = {
7957                 cls : 'has-feedback',
7958                 cn :  [
7959                     input,
7960                     feedback
7961                 ] 
7962             };  
7963         }
7964         
7965         if (this.before || this.after) {
7966             
7967             inputblock = {
7968                 cls : 'input-group',
7969                 cn :  [] 
7970             };
7971             
7972             if (this.before && typeof(this.before) == 'string') {
7973                 
7974                 inputblock.cn.push({
7975                     tag :'span',
7976                     cls : 'roo-input-before input-group-addon',
7977                     html : this.before
7978                 });
7979             }
7980             if (this.before && typeof(this.before) == 'object') {
7981                 this.before = Roo.factory(this.before);
7982                 
7983                 inputblock.cn.push({
7984                     tag :'span',
7985                     cls : 'roo-input-before input-group-' +
7986                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7987                 });
7988             }
7989             
7990             inputblock.cn.push(input);
7991             
7992             if (this.after && typeof(this.after) == 'string') {
7993                 inputblock.cn.push({
7994                     tag :'span',
7995                     cls : 'roo-input-after input-group-addon',
7996                     html : this.after
7997                 });
7998             }
7999             if (this.after && typeof(this.after) == 'object') {
8000                 this.after = Roo.factory(this.after);
8001                 
8002                 inputblock.cn.push({
8003                     tag :'span',
8004                     cls : 'roo-input-after input-group-' +
8005                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8006                 });
8007             }
8008             
8009             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8010                 inputblock.cls += ' has-feedback';
8011                 inputblock.cn.push(feedback);
8012             }
8013         };
8014         
8015         if (align ==='left' && this.fieldLabel.length) {
8016                 
8017                 cfg.cn = [
8018                     
8019                     {
8020                         tag: 'label',
8021                         'for' :  id,
8022                         cls : 'control-label col-sm-' + this.labelWidth,
8023                         html : this.fieldLabel
8024                         
8025                     },
8026                     {
8027                         cls : "col-sm-" + (12 - this.labelWidth), 
8028                         cn: [
8029                             inputblock
8030                         ]
8031                     }
8032                     
8033                 ];
8034         } else if ( this.fieldLabel.length) {
8035                 
8036                  cfg.cn = [
8037                    
8038                     {
8039                         tag: 'label',
8040                         //cls : 'input-group-addon',
8041                         html : this.fieldLabel
8042                         
8043                     },
8044                     
8045                     inputblock
8046                     
8047                 ];
8048
8049         } else {
8050             
8051                 cfg.cn = [
8052                     
8053                         inputblock
8054                     
8055                 ];
8056                 
8057                 
8058         };
8059         
8060         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8061            cfg.cls += ' navbar-form';
8062         }
8063         
8064         return cfg;
8065         
8066     },
8067     /**
8068      * return the real input element.
8069      */
8070     inputEl: function ()
8071     {
8072         return this.el.select('input.form-control',true).first();
8073     },
8074     
8075     tooltipEl : function()
8076     {
8077         return this.inputEl();
8078     },
8079     
8080     setDisabled : function(v)
8081     {
8082         var i  = this.inputEl().dom;
8083         if (!v) {
8084             i.removeAttribute('disabled');
8085             return;
8086             
8087         }
8088         i.setAttribute('disabled','true');
8089     },
8090     initEvents : function()
8091     {
8092           
8093         this.inputEl().on("keydown" , this.fireKey,  this);
8094         this.inputEl().on("focus", this.onFocus,  this);
8095         this.inputEl().on("blur", this.onBlur,  this);
8096         
8097         this.inputEl().relayEvent('keyup', this);
8098  
8099         // reference to original value for reset
8100         this.originalValue = this.getValue();
8101         //Roo.form.TextField.superclass.initEvents.call(this);
8102         if(this.validationEvent == 'keyup'){
8103             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8104             this.inputEl().on('keyup', this.filterValidation, this);
8105         }
8106         else if(this.validationEvent !== false){
8107             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8108         }
8109         
8110         if(this.selectOnFocus){
8111             this.on("focus", this.preFocus, this);
8112             
8113         }
8114         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8115             this.inputEl().on("keypress", this.filterKeys, this);
8116         }
8117        /* if(this.grow){
8118             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8119             this.el.on("click", this.autoSize,  this);
8120         }
8121         */
8122         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8123             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8124         }
8125         
8126         if (typeof(this.before) == 'object') {
8127             this.before.render(this.el.select('.roo-input-before',true).first());
8128         }
8129         if (typeof(this.after) == 'object') {
8130             this.after.render(this.el.select('.roo-input-after',true).first());
8131         }
8132         
8133         
8134     },
8135     filterValidation : function(e){
8136         if(!e.isNavKeyPress()){
8137             this.validationTask.delay(this.validationDelay);
8138         }
8139     },
8140      /**
8141      * Validates the field value
8142      * @return {Boolean} True if the value is valid, else false
8143      */
8144     validate : function(){
8145         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8146         if(this.disabled || this.validateValue(this.getRawValue())){
8147             this.markValid();
8148             return true;
8149         }
8150         
8151         this.markInvalid();
8152         return false;
8153     },
8154     
8155     
8156     /**
8157      * Validates a value according to the field's validation rules and marks the field as invalid
8158      * if the validation fails
8159      * @param {Mixed} value The value to validate
8160      * @return {Boolean} True if the value is valid, else false
8161      */
8162     validateValue : function(value){
8163         if(value.length < 1)  { // if it's blank
8164             if(this.allowBlank){
8165                 return true;
8166             }
8167             return false;
8168         }
8169         
8170         if(value.length < this.minLength){
8171             return false;
8172         }
8173         if(value.length > this.maxLength){
8174             return false;
8175         }
8176         if(this.vtype){
8177             var vt = Roo.form.VTypes;
8178             if(!vt[this.vtype](value, this)){
8179                 return false;
8180             }
8181         }
8182         if(typeof this.validator == "function"){
8183             var msg = this.validator(value);
8184             if(msg !== true){
8185                 return false;
8186             }
8187         }
8188         
8189         if(this.regex && !this.regex.test(value)){
8190             return false;
8191         }
8192         
8193         return true;
8194     },
8195
8196     
8197     
8198      // private
8199     fireKey : function(e){
8200         //Roo.log('field ' + e.getKey());
8201         if(e.isNavKeyPress()){
8202             this.fireEvent("specialkey", this, e);
8203         }
8204     },
8205     focus : function (selectText){
8206         if(this.rendered){
8207             this.inputEl().focus();
8208             if(selectText === true){
8209                 this.inputEl().dom.select();
8210             }
8211         }
8212         return this;
8213     } ,
8214     
8215     onFocus : function(){
8216         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8217            // this.el.addClass(this.focusClass);
8218         }
8219         if(!this.hasFocus){
8220             this.hasFocus = true;
8221             this.startValue = this.getValue();
8222             this.fireEvent("focus", this);
8223         }
8224     },
8225     
8226     beforeBlur : Roo.emptyFn,
8227
8228     
8229     // private
8230     onBlur : function(){
8231         this.beforeBlur();
8232         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8233             //this.el.removeClass(this.focusClass);
8234         }
8235         this.hasFocus = false;
8236         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8237             this.validate();
8238         }
8239         var v = this.getValue();
8240         if(String(v) !== String(this.startValue)){
8241             this.fireEvent('change', this, v, this.startValue);
8242         }
8243         this.fireEvent("blur", this);
8244     },
8245     
8246     /**
8247      * Resets the current field value to the originally loaded value and clears any validation messages
8248      */
8249     reset : function(){
8250         this.setValue(this.originalValue);
8251         this.validate();
8252     },
8253      /**
8254      * Returns the name of the field
8255      * @return {Mixed} name The name field
8256      */
8257     getName: function(){
8258         return this.name;
8259     },
8260      /**
8261      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8262      * @return {Mixed} value The field value
8263      */
8264     getValue : function(){
8265         
8266         var v = this.inputEl().getValue();
8267         
8268         return v;
8269     },
8270     /**
8271      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8272      * @return {Mixed} value The field value
8273      */
8274     getRawValue : function(){
8275         var v = this.inputEl().getValue();
8276         
8277         return v;
8278     },
8279     
8280     /**
8281      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8282      * @param {Mixed} value The value to set
8283      */
8284     setRawValue : function(v){
8285         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8286     },
8287     
8288     selectText : function(start, end){
8289         var v = this.getRawValue();
8290         if(v.length > 0){
8291             start = start === undefined ? 0 : start;
8292             end = end === undefined ? v.length : end;
8293             var d = this.inputEl().dom;
8294             if(d.setSelectionRange){
8295                 d.setSelectionRange(start, end);
8296             }else if(d.createTextRange){
8297                 var range = d.createTextRange();
8298                 range.moveStart("character", start);
8299                 range.moveEnd("character", v.length-end);
8300                 range.select();
8301             }
8302         }
8303     },
8304     
8305     /**
8306      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8307      * @param {Mixed} value The value to set
8308      */
8309     setValue : function(v){
8310         this.value = v;
8311         if(this.rendered){
8312             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8313             this.validate();
8314         }
8315     },
8316     
8317     /*
8318     processValue : function(value){
8319         if(this.stripCharsRe){
8320             var newValue = value.replace(this.stripCharsRe, '');
8321             if(newValue !== value){
8322                 this.setRawValue(newValue);
8323                 return newValue;
8324             }
8325         }
8326         return value;
8327     },
8328   */
8329     preFocus : function(){
8330         
8331         if(this.selectOnFocus){
8332             this.inputEl().dom.select();
8333         }
8334     },
8335     filterKeys : function(e){
8336         var k = e.getKey();
8337         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8338             return;
8339         }
8340         var c = e.getCharCode(), cc = String.fromCharCode(c);
8341         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8342             return;
8343         }
8344         if(!this.maskRe.test(cc)){
8345             e.stopEvent();
8346         }
8347     },
8348      /**
8349      * Clear any invalid styles/messages for this field
8350      */
8351     clearInvalid : function(){
8352         
8353         if(!this.el || this.preventMark){ // not rendered
8354             return;
8355         }
8356         this.el.removeClass(this.invalidClass);
8357         
8358         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8359             
8360             var feedback = this.el.select('.form-control-feedback', true).first();
8361             
8362             if(feedback){
8363                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8364             }
8365             
8366         }
8367         
8368         this.fireEvent('valid', this);
8369     },
8370     
8371      /**
8372      * Mark this field as valid
8373      */
8374     markValid : function()
8375     {
8376         if(!this.el  || this.preventMark){ // not rendered
8377             return;
8378         }
8379         
8380         this.el.removeClass([this.invalidClass, this.validClass]);
8381         
8382         var feedback = this.el.select('.form-control-feedback', true).first();
8383             
8384         if(feedback){
8385             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8386         }
8387
8388         if(this.disabled || this.allowBlank){
8389             return;
8390         }
8391         
8392         var formGroup = this.el.findParent('.form-group', false, true);
8393         
8394         if(formGroup){
8395             
8396             var label = formGroup.select('label', true).first();
8397             var icon = formGroup.select('i.fa-star', true).first();
8398             
8399             if(label && icon){
8400                 icon.remove();
8401             }
8402         }
8403         
8404         this.el.addClass(this.validClass);
8405         
8406         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8407             
8408             var feedback = this.el.select('.form-control-feedback', true).first();
8409             
8410             if(feedback){
8411                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8412                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8413             }
8414             
8415         }
8416         
8417         this.fireEvent('valid', this);
8418     },
8419     
8420      /**
8421      * Mark this field as invalid
8422      * @param {String} msg The validation message
8423      */
8424     markInvalid : function(msg)
8425     {
8426         if(!this.el  || this.preventMark){ // not rendered
8427             return;
8428         }
8429         
8430         this.el.removeClass([this.invalidClass, this.validClass]);
8431         
8432         var feedback = this.el.select('.form-control-feedback', true).first();
8433             
8434         if(feedback){
8435             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8436         }
8437
8438         if(this.disabled || this.allowBlank){
8439             return;
8440         }
8441         
8442         var formGroup = this.el.findParent('.form-group', false, true);
8443         
8444         if(formGroup){
8445             var label = formGroup.select('label', true).first();
8446             var icon = formGroup.select('i.fa-star', true).first();
8447
8448             if(!this.getValue().length && label && !icon){
8449                 this.el.findParent('.form-group', false, true).createChild({
8450                     tag : 'i',
8451                     cls : 'text-danger fa fa-lg fa-star',
8452                     tooltip : 'This field is required',
8453                     style : 'margin-right:5px;'
8454                 }, label, true);
8455             }
8456         }
8457         
8458         
8459         this.el.addClass(this.invalidClass);
8460         
8461         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8462             
8463             var feedback = this.el.select('.form-control-feedback', true).first();
8464             
8465             if(feedback){
8466                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8467                 
8468                 if(this.getValue().length || this.forceFeedback){
8469                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8470                 }
8471                 
8472             }
8473             
8474         }
8475         
8476         this.fireEvent('invalid', this, msg);
8477     },
8478     // private
8479     SafariOnKeyDown : function(event)
8480     {
8481         // this is a workaround for a password hang bug on chrome/ webkit.
8482         
8483         var isSelectAll = false;
8484         
8485         if(this.inputEl().dom.selectionEnd > 0){
8486             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8487         }
8488         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8489             event.preventDefault();
8490             this.setValue('');
8491             return;
8492         }
8493         
8494         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8495             
8496             event.preventDefault();
8497             // this is very hacky as keydown always get's upper case.
8498             //
8499             var cc = String.fromCharCode(event.getCharCode());
8500             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8501             
8502         }
8503     },
8504     adjustWidth : function(tag, w){
8505         tag = tag.toLowerCase();
8506         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8507             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8508                 if(tag == 'input'){
8509                     return w + 2;
8510                 }
8511                 if(tag == 'textarea'){
8512                     return w-2;
8513                 }
8514             }else if(Roo.isOpera){
8515                 if(tag == 'input'){
8516                     return w + 2;
8517                 }
8518                 if(tag == 'textarea'){
8519                     return w-2;
8520                 }
8521             }
8522         }
8523         return w;
8524     }
8525     
8526 });
8527
8528  
8529 /*
8530  * - LGPL
8531  *
8532  * Input
8533  * 
8534  */
8535
8536 /**
8537  * @class Roo.bootstrap.TextArea
8538  * @extends Roo.bootstrap.Input
8539  * Bootstrap TextArea class
8540  * @cfg {Number} cols Specifies the visible width of a text area
8541  * @cfg {Number} rows Specifies the visible number of lines in a text area
8542  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8543  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8544  * @cfg {string} html text
8545  * 
8546  * @constructor
8547  * Create a new TextArea
8548  * @param {Object} config The config object
8549  */
8550
8551 Roo.bootstrap.TextArea = function(config){
8552     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8553    
8554 };
8555
8556 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8557      
8558     cols : false,
8559     rows : 5,
8560     readOnly : false,
8561     warp : 'soft',
8562     resize : false,
8563     value: false,
8564     html: false,
8565     
8566     getAutoCreate : function(){
8567         
8568         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8569         
8570         var id = Roo.id();
8571         
8572         var cfg = {};
8573         
8574         var input =  {
8575             tag: 'textarea',
8576             id : id,
8577             warp : this.warp,
8578             rows : this.rows,
8579             value : this.value || '',
8580             html: this.html || '',
8581             cls : 'form-control',
8582             placeholder : this.placeholder || '' 
8583             
8584         };
8585         
8586         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8587             input.maxLength = this.maxLength;
8588         }
8589         
8590         if(this.resize){
8591             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8592         }
8593         
8594         if(this.cols){
8595             input.cols = this.cols;
8596         }
8597         
8598         if (this.readOnly) {
8599             input.readonly = true;
8600         }
8601         
8602         if (this.name) {
8603             input.name = this.name;
8604         }
8605         
8606         if (this.size) {
8607             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8608         }
8609         
8610         var settings=this;
8611         ['xs','sm','md','lg'].map(function(size){
8612             if (settings[size]) {
8613                 cfg.cls += ' col-' + size + '-' + settings[size];
8614             }
8615         });
8616         
8617         var inputblock = input;
8618         
8619         if(this.hasFeedback && !this.allowBlank){
8620             
8621             var feedback = {
8622                 tag: 'span',
8623                 cls: 'glyphicon form-control-feedback'
8624             };
8625
8626             inputblock = {
8627                 cls : 'has-feedback',
8628                 cn :  [
8629                     input,
8630                     feedback
8631                 ] 
8632             };  
8633         }
8634         
8635         
8636         if (this.before || this.after) {
8637             
8638             inputblock = {
8639                 cls : 'input-group',
8640                 cn :  [] 
8641             };
8642             if (this.before) {
8643                 inputblock.cn.push({
8644                     tag :'span',
8645                     cls : 'input-group-addon',
8646                     html : this.before
8647                 });
8648             }
8649             
8650             inputblock.cn.push(input);
8651             
8652             if(this.hasFeedback && !this.allowBlank){
8653                 inputblock.cls += ' has-feedback';
8654                 inputblock.cn.push(feedback);
8655             }
8656             
8657             if (this.after) {
8658                 inputblock.cn.push({
8659                     tag :'span',
8660                     cls : 'input-group-addon',
8661                     html : this.after
8662                 });
8663             }
8664             
8665         }
8666         
8667         if (align ==='left' && this.fieldLabel.length) {
8668 //                Roo.log("left and has label");
8669                 cfg.cn = [
8670                     
8671                     {
8672                         tag: 'label',
8673                         'for' :  id,
8674                         cls : 'control-label col-sm-' + this.labelWidth,
8675                         html : this.fieldLabel
8676                         
8677                     },
8678                     {
8679                         cls : "col-sm-" + (12 - this.labelWidth), 
8680                         cn: [
8681                             inputblock
8682                         ]
8683                     }
8684                     
8685                 ];
8686         } else if ( this.fieldLabel.length) {
8687 //                Roo.log(" label");
8688                  cfg.cn = [
8689                    
8690                     {
8691                         tag: 'label',
8692                         //cls : 'input-group-addon',
8693                         html : this.fieldLabel
8694                         
8695                     },
8696                     
8697                     inputblock
8698                     
8699                 ];
8700
8701         } else {
8702             
8703 //                   Roo.log(" no label && no align");
8704                 cfg.cn = [
8705                     
8706                         inputblock
8707                     
8708                 ];
8709                 
8710                 
8711         }
8712         
8713         if (this.disabled) {
8714             input.disabled=true;
8715         }
8716         
8717         return cfg;
8718         
8719     },
8720     /**
8721      * return the real textarea element.
8722      */
8723     inputEl: function ()
8724     {
8725         return this.el.select('textarea.form-control',true).first();
8726     },
8727     
8728     /**
8729      * Clear any invalid styles/messages for this field
8730      */
8731     clearInvalid : function()
8732     {
8733         
8734         if(!this.el || this.preventMark){ // not rendered
8735             return;
8736         }
8737         
8738         var label = this.el.select('label', true).first();
8739         var icon = this.el.select('i.fa-star', true).first();
8740         
8741         if(label && icon){
8742             icon.remove();
8743         }
8744         
8745         this.el.removeClass(this.invalidClass);
8746         
8747         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8748             
8749             var feedback = this.el.select('.form-control-feedback', true).first();
8750             
8751             if(feedback){
8752                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8753             }
8754             
8755         }
8756         
8757         this.fireEvent('valid', this);
8758     },
8759     
8760      /**
8761      * Mark this field as valid
8762      */
8763     markValid : function()
8764     {
8765         if(!this.el  || this.preventMark){ // not rendered
8766             return;
8767         }
8768         
8769         this.el.removeClass([this.invalidClass, this.validClass]);
8770         
8771         var feedback = this.el.select('.form-control-feedback', true).first();
8772             
8773         if(feedback){
8774             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8775         }
8776
8777         if(this.disabled || this.allowBlank){
8778             return;
8779         }
8780         
8781         var label = this.el.select('label', true).first();
8782         var icon = this.el.select('i.fa-star', true).first();
8783         
8784         if(label && icon){
8785             icon.remove();
8786         }
8787         
8788         this.el.addClass(this.validClass);
8789         
8790         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8791             
8792             var feedback = this.el.select('.form-control-feedback', true).first();
8793             
8794             if(feedback){
8795                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8796                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8797             }
8798             
8799         }
8800         
8801         this.fireEvent('valid', this);
8802     },
8803     
8804      /**
8805      * Mark this field as invalid
8806      * @param {String} msg The validation message
8807      */
8808     markInvalid : function(msg)
8809     {
8810         if(!this.el  || this.preventMark){ // not rendered
8811             return;
8812         }
8813         
8814         this.el.removeClass([this.invalidClass, this.validClass]);
8815         
8816         var feedback = this.el.select('.form-control-feedback', true).first();
8817             
8818         if(feedback){
8819             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8820         }
8821
8822         if(this.disabled || this.allowBlank){
8823             return;
8824         }
8825         
8826         var label = this.el.select('label', true).first();
8827         var icon = this.el.select('i.fa-star', true).first();
8828         
8829         if(!this.getValue().length && label && !icon){
8830             this.el.createChild({
8831                 tag : 'i',
8832                 cls : 'text-danger fa fa-lg fa-star',
8833                 tooltip : 'This field is required',
8834                 style : 'margin-right:5px;'
8835             }, label, true);
8836         }
8837
8838         this.el.addClass(this.invalidClass);
8839         
8840         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8841             
8842             var feedback = this.el.select('.form-control-feedback', true).first();
8843             
8844             if(feedback){
8845                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8846                 
8847                 if(this.getValue().length || this.forceFeedback){
8848                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8849                 }
8850                 
8851             }
8852             
8853         }
8854         
8855         this.fireEvent('invalid', this, msg);
8856     }
8857 });
8858
8859  
8860 /*
8861  * - LGPL
8862  *
8863  * trigger field - base class for combo..
8864  * 
8865  */
8866  
8867 /**
8868  * @class Roo.bootstrap.TriggerField
8869  * @extends Roo.bootstrap.Input
8870  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8871  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8872  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8873  * for which you can provide a custom implementation.  For example:
8874  * <pre><code>
8875 var trigger = new Roo.bootstrap.TriggerField();
8876 trigger.onTriggerClick = myTriggerFn;
8877 trigger.applyTo('my-field');
8878 </code></pre>
8879  *
8880  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8881  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8882  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8883  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8884  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8885
8886  * @constructor
8887  * Create a new TriggerField.
8888  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8889  * to the base TextField)
8890  */
8891 Roo.bootstrap.TriggerField = function(config){
8892     this.mimicing = false;
8893     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8894 };
8895
8896 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8897     /**
8898      * @cfg {String} triggerClass A CSS class to apply to the trigger
8899      */
8900      /**
8901      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8902      */
8903     hideTrigger:false,
8904
8905     /**
8906      * @cfg {Boolean} removable (true|false) special filter default false
8907      */
8908     removable : false,
8909     
8910     /** @cfg {Boolean} grow @hide */
8911     /** @cfg {Number} growMin @hide */
8912     /** @cfg {Number} growMax @hide */
8913
8914     /**
8915      * @hide 
8916      * @method
8917      */
8918     autoSize: Roo.emptyFn,
8919     // private
8920     monitorTab : true,
8921     // private
8922     deferHeight : true,
8923
8924     
8925     actionMode : 'wrap',
8926     
8927     caret : false,
8928     
8929     
8930     getAutoCreate : function(){
8931        
8932         var align = this.labelAlign || this.parentLabelAlign();
8933         
8934         var id = Roo.id();
8935         
8936         var cfg = {
8937             cls: 'form-group' //input-group
8938         };
8939         
8940         
8941         var input =  {
8942             tag: 'input',
8943             id : id,
8944             type : this.inputType,
8945             cls : 'form-control',
8946             autocomplete: 'new-password',
8947             placeholder : this.placeholder || '' 
8948             
8949         };
8950         if (this.name) {
8951             input.name = this.name;
8952         }
8953         if (this.size) {
8954             input.cls += ' input-' + this.size;
8955         }
8956         
8957         if (this.disabled) {
8958             input.disabled=true;
8959         }
8960         
8961         var inputblock = input;
8962         
8963         if(this.hasFeedback && !this.allowBlank){
8964             
8965             var feedback = {
8966                 tag: 'span',
8967                 cls: 'glyphicon form-control-feedback'
8968             };
8969             
8970             if(this.removable && !this.editable && !this.tickable){
8971                 inputblock = {
8972                     cls : 'has-feedback',
8973                     cn :  [
8974                         inputblock,
8975                         {
8976                             tag: 'button',
8977                             html : 'x',
8978                             cls : 'roo-combo-removable-btn close'
8979                         },
8980                         feedback
8981                     ] 
8982                 };
8983             } else {
8984                 inputblock = {
8985                     cls : 'has-feedback',
8986                     cn :  [
8987                         inputblock,
8988                         feedback
8989                     ] 
8990                 };
8991             }
8992
8993         } else {
8994             if(this.removable && !this.editable && !this.tickable){
8995                 inputblock = {
8996                     cls : 'roo-removable',
8997                     cn :  [
8998                         inputblock,
8999                         {
9000                             tag: 'button',
9001                             html : 'x',
9002                             cls : 'roo-combo-removable-btn close'
9003                         }
9004                     ] 
9005                 };
9006             }
9007         }
9008         
9009         if (this.before || this.after) {
9010             
9011             inputblock = {
9012                 cls : 'input-group',
9013                 cn :  [] 
9014             };
9015             if (this.before) {
9016                 inputblock.cn.push({
9017                     tag :'span',
9018                     cls : 'input-group-addon',
9019                     html : this.before
9020                 });
9021             }
9022             
9023             inputblock.cn.push(input);
9024             
9025             if(this.hasFeedback && !this.allowBlank){
9026                 inputblock.cls += ' has-feedback';
9027                 inputblock.cn.push(feedback);
9028             }
9029             
9030             if (this.after) {
9031                 inputblock.cn.push({
9032                     tag :'span',
9033                     cls : 'input-group-addon',
9034                     html : this.after
9035                 });
9036             }
9037             
9038         };
9039         
9040         var box = {
9041             tag: 'div',
9042             cn: [
9043                 {
9044                     tag: 'input',
9045                     type : 'hidden',
9046                     cls: 'form-hidden-field'
9047                 },
9048                 inputblock
9049             ]
9050             
9051         };
9052         
9053         if(this.multiple){
9054             box = {
9055                 tag: 'div',
9056                 cn: [
9057                     {
9058                         tag: 'input',
9059                         type : 'hidden',
9060                         cls: 'form-hidden-field'
9061                     },
9062                     {
9063                         tag: 'ul',
9064                         cls: 'select2-choices',
9065                         cn:[
9066                             {
9067                                 tag: 'li',
9068                                 cls: 'select2-search-field',
9069                                 cn: [
9070
9071                                     inputblock
9072                                 ]
9073                             }
9074                         ]
9075                     }
9076                 ]
9077             }
9078         };
9079         
9080         var combobox = {
9081             cls: 'select2-container input-group',
9082             cn: [
9083                 box
9084 //                {
9085 //                    tag: 'ul',
9086 //                    cls: 'typeahead typeahead-long dropdown-menu',
9087 //                    style: 'display:none'
9088 //                }
9089             ]
9090         };
9091         
9092         if(!this.multiple && this.showToggleBtn){
9093             
9094             var caret = {
9095                         tag: 'span',
9096                         cls: 'caret'
9097              };
9098             if (this.caret != false) {
9099                 caret = {
9100                      tag: 'i',
9101                      cls: 'fa fa-' + this.caret
9102                 };
9103                 
9104             }
9105             
9106             combobox.cn.push({
9107                 tag :'span',
9108                 cls : 'input-group-addon btn dropdown-toggle',
9109                 cn : [
9110                     caret,
9111                     {
9112                         tag: 'span',
9113                         cls: 'combobox-clear',
9114                         cn  : [
9115                             {
9116                                 tag : 'i',
9117                                 cls: 'icon-remove'
9118                             }
9119                         ]
9120                     }
9121                 ]
9122
9123             })
9124         }
9125         
9126         if(this.multiple){
9127             combobox.cls += ' select2-container-multi';
9128         }
9129         
9130         if (align ==='left' && this.fieldLabel.length) {
9131             
9132 //                Roo.log("left and has label");
9133                 cfg.cn = [
9134                     
9135                     {
9136                         tag: 'label',
9137                         'for' :  id,
9138                         cls : 'control-label col-sm-' + this.labelWidth,
9139                         html : this.fieldLabel
9140                         
9141                     },
9142                     {
9143                         cls : "col-sm-" + (12 - this.labelWidth), 
9144                         cn: [
9145                             combobox
9146                         ]
9147                     }
9148                     
9149                 ];
9150         } else if ( this.fieldLabel.length) {
9151 //                Roo.log(" label");
9152                  cfg.cn = [
9153                    
9154                     {
9155                         tag: 'label',
9156                         //cls : 'input-group-addon',
9157                         html : this.fieldLabel
9158                         
9159                     },
9160                     
9161                     combobox
9162                     
9163                 ];
9164
9165         } else {
9166             
9167 //                Roo.log(" no label && no align");
9168                 cfg = combobox
9169                      
9170                 
9171         }
9172          
9173         var settings=this;
9174         ['xs','sm','md','lg'].map(function(size){
9175             if (settings[size]) {
9176                 cfg.cls += ' col-' + size + '-' + settings[size];
9177             }
9178         });
9179         
9180         return cfg;
9181         
9182     },
9183     
9184     
9185     
9186     // private
9187     onResize : function(w, h){
9188 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9189 //        if(typeof w == 'number'){
9190 //            var x = w - this.trigger.getWidth();
9191 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9192 //            this.trigger.setStyle('left', x+'px');
9193 //        }
9194     },
9195
9196     // private
9197     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9198
9199     // private
9200     getResizeEl : function(){
9201         return this.inputEl();
9202     },
9203
9204     // private
9205     getPositionEl : function(){
9206         return this.inputEl();
9207     },
9208
9209     // private
9210     alignErrorIcon : function(){
9211         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9212     },
9213
9214     // private
9215     initEvents : function(){
9216         
9217         this.createList();
9218         
9219         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9220         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9221         if(!this.multiple && this.showToggleBtn){
9222             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9223             if(this.hideTrigger){
9224                 this.trigger.setDisplayed(false);
9225             }
9226             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9227         }
9228         
9229         if(this.multiple){
9230             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9231         }
9232         
9233         if(this.removable && !this.editable && !this.tickable){
9234             var close = this.closeTriggerEl();
9235             
9236             if(close){
9237                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9238                 close.on('click', this.removeBtnClick, this, close);
9239             }
9240         }
9241         
9242         //this.trigger.addClassOnOver('x-form-trigger-over');
9243         //this.trigger.addClassOnClick('x-form-trigger-click');
9244         
9245         //if(!this.width){
9246         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9247         //}
9248     },
9249     
9250     closeTriggerEl : function()
9251     {
9252         var close = this.el.select('.roo-combo-removable-btn', true).first();
9253         return close ? close : false;
9254     },
9255     
9256     removeBtnClick : function(e, h, el)
9257     {
9258         e.preventDefault();
9259         
9260         if(this.fireEvent("remove", this) !== false){
9261             this.reset();
9262         }
9263     },
9264     
9265     createList : function()
9266     {
9267         this.list = Roo.get(document.body).createChild({
9268             tag: 'ul',
9269             cls: 'typeahead typeahead-long dropdown-menu',
9270             style: 'display:none'
9271         });
9272         
9273         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9274         
9275     },
9276
9277     // private
9278     initTrigger : function(){
9279        
9280     },
9281
9282     // private
9283     onDestroy : function(){
9284         if(this.trigger){
9285             this.trigger.removeAllListeners();
9286           //  this.trigger.remove();
9287         }
9288         //if(this.wrap){
9289         //    this.wrap.remove();
9290         //}
9291         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9292     },
9293
9294     // private
9295     onFocus : function(){
9296         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9297         /*
9298         if(!this.mimicing){
9299             this.wrap.addClass('x-trigger-wrap-focus');
9300             this.mimicing = true;
9301             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9302             if(this.monitorTab){
9303                 this.el.on("keydown", this.checkTab, this);
9304             }
9305         }
9306         */
9307     },
9308
9309     // private
9310     checkTab : function(e){
9311         if(e.getKey() == e.TAB){
9312             this.triggerBlur();
9313         }
9314     },
9315
9316     // private
9317     onBlur : function(){
9318         // do nothing
9319     },
9320
9321     // private
9322     mimicBlur : function(e, t){
9323         /*
9324         if(!this.wrap.contains(t) && this.validateBlur()){
9325             this.triggerBlur();
9326         }
9327         */
9328     },
9329
9330     // private
9331     triggerBlur : function(){
9332         this.mimicing = false;
9333         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9334         if(this.monitorTab){
9335             this.el.un("keydown", this.checkTab, this);
9336         }
9337         //this.wrap.removeClass('x-trigger-wrap-focus');
9338         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9339     },
9340
9341     // private
9342     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9343     validateBlur : function(e, t){
9344         return true;
9345     },
9346
9347     // private
9348     onDisable : function(){
9349         this.inputEl().dom.disabled = true;
9350         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9351         //if(this.wrap){
9352         //    this.wrap.addClass('x-item-disabled');
9353         //}
9354     },
9355
9356     // private
9357     onEnable : function(){
9358         this.inputEl().dom.disabled = false;
9359         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9360         //if(this.wrap){
9361         //    this.el.removeClass('x-item-disabled');
9362         //}
9363     },
9364
9365     // private
9366     onShow : function(){
9367         var ae = this.getActionEl();
9368         
9369         if(ae){
9370             ae.dom.style.display = '';
9371             ae.dom.style.visibility = 'visible';
9372         }
9373     },
9374
9375     // private
9376     
9377     onHide : function(){
9378         var ae = this.getActionEl();
9379         ae.dom.style.display = 'none';
9380     },
9381
9382     /**
9383      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9384      * by an implementing function.
9385      * @method
9386      * @param {EventObject} e
9387      */
9388     onTriggerClick : Roo.emptyFn
9389 });
9390  /*
9391  * Based on:
9392  * Ext JS Library 1.1.1
9393  * Copyright(c) 2006-2007, Ext JS, LLC.
9394  *
9395  * Originally Released Under LGPL - original licence link has changed is not relivant.
9396  *
9397  * Fork - LGPL
9398  * <script type="text/javascript">
9399  */
9400
9401
9402 /**
9403  * @class Roo.data.SortTypes
9404  * @singleton
9405  * Defines the default sorting (casting?) comparison functions used when sorting data.
9406  */
9407 Roo.data.SortTypes = {
9408     /**
9409      * Default sort that does nothing
9410      * @param {Mixed} s The value being converted
9411      * @return {Mixed} The comparison value
9412      */
9413     none : function(s){
9414         return s;
9415     },
9416     
9417     /**
9418      * The regular expression used to strip tags
9419      * @type {RegExp}
9420      * @property
9421      */
9422     stripTagsRE : /<\/?[^>]+>/gi,
9423     
9424     /**
9425      * Strips all HTML tags to sort on text only
9426      * @param {Mixed} s The value being converted
9427      * @return {String} The comparison value
9428      */
9429     asText : function(s){
9430         return String(s).replace(this.stripTagsRE, "");
9431     },
9432     
9433     /**
9434      * Strips all HTML tags to sort on text only - Case insensitive
9435      * @param {Mixed} s The value being converted
9436      * @return {String} The comparison value
9437      */
9438     asUCText : function(s){
9439         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9440     },
9441     
9442     /**
9443      * Case insensitive string
9444      * @param {Mixed} s The value being converted
9445      * @return {String} The comparison value
9446      */
9447     asUCString : function(s) {
9448         return String(s).toUpperCase();
9449     },
9450     
9451     /**
9452      * Date sorting
9453      * @param {Mixed} s The value being converted
9454      * @return {Number} The comparison value
9455      */
9456     asDate : function(s) {
9457         if(!s){
9458             return 0;
9459         }
9460         if(s instanceof Date){
9461             return s.getTime();
9462         }
9463         return Date.parse(String(s));
9464     },
9465     
9466     /**
9467      * Float sorting
9468      * @param {Mixed} s The value being converted
9469      * @return {Float} The comparison value
9470      */
9471     asFloat : function(s) {
9472         var val = parseFloat(String(s).replace(/,/g, ""));
9473         if(isNaN(val)) {
9474             val = 0;
9475         }
9476         return val;
9477     },
9478     
9479     /**
9480      * Integer sorting
9481      * @param {Mixed} s The value being converted
9482      * @return {Number} The comparison value
9483      */
9484     asInt : function(s) {
9485         var val = parseInt(String(s).replace(/,/g, ""));
9486         if(isNaN(val)) {
9487             val = 0;
9488         }
9489         return val;
9490     }
9491 };/*
9492  * Based on:
9493  * Ext JS Library 1.1.1
9494  * Copyright(c) 2006-2007, Ext JS, LLC.
9495  *
9496  * Originally Released Under LGPL - original licence link has changed is not relivant.
9497  *
9498  * Fork - LGPL
9499  * <script type="text/javascript">
9500  */
9501
9502 /**
9503 * @class Roo.data.Record
9504  * Instances of this class encapsulate both record <em>definition</em> information, and record
9505  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9506  * to access Records cached in an {@link Roo.data.Store} object.<br>
9507  * <p>
9508  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9509  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9510  * objects.<br>
9511  * <p>
9512  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9513  * @constructor
9514  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9515  * {@link #create}. The parameters are the same.
9516  * @param {Array} data An associative Array of data values keyed by the field name.
9517  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9518  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9519  * not specified an integer id is generated.
9520  */
9521 Roo.data.Record = function(data, id){
9522     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9523     this.data = data;
9524 };
9525
9526 /**
9527  * Generate a constructor for a specific record layout.
9528  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9529  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9530  * Each field definition object may contain the following properties: <ul>
9531  * <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,
9532  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9533  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9534  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9535  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9536  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9537  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9538  * this may be omitted.</p></li>
9539  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9540  * <ul><li>auto (Default, implies no conversion)</li>
9541  * <li>string</li>
9542  * <li>int</li>
9543  * <li>float</li>
9544  * <li>boolean</li>
9545  * <li>date</li></ul></p></li>
9546  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9547  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9548  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9549  * by the Reader into an object that will be stored in the Record. It is passed the
9550  * following parameters:<ul>
9551  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9552  * </ul></p></li>
9553  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9554  * </ul>
9555  * <br>usage:<br><pre><code>
9556 var TopicRecord = Roo.data.Record.create(
9557     {name: 'title', mapping: 'topic_title'},
9558     {name: 'author', mapping: 'username'},
9559     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9560     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9561     {name: 'lastPoster', mapping: 'user2'},
9562     {name: 'excerpt', mapping: 'post_text'}
9563 );
9564
9565 var myNewRecord = new TopicRecord({
9566     title: 'Do my job please',
9567     author: 'noobie',
9568     totalPosts: 1,
9569     lastPost: new Date(),
9570     lastPoster: 'Animal',
9571     excerpt: 'No way dude!'
9572 });
9573 myStore.add(myNewRecord);
9574 </code></pre>
9575  * @method create
9576  * @static
9577  */
9578 Roo.data.Record.create = function(o){
9579     var f = function(){
9580         f.superclass.constructor.apply(this, arguments);
9581     };
9582     Roo.extend(f, Roo.data.Record);
9583     var p = f.prototype;
9584     p.fields = new Roo.util.MixedCollection(false, function(field){
9585         return field.name;
9586     });
9587     for(var i = 0, len = o.length; i < len; i++){
9588         p.fields.add(new Roo.data.Field(o[i]));
9589     }
9590     f.getField = function(name){
9591         return p.fields.get(name);  
9592     };
9593     return f;
9594 };
9595
9596 Roo.data.Record.AUTO_ID = 1000;
9597 Roo.data.Record.EDIT = 'edit';
9598 Roo.data.Record.REJECT = 'reject';
9599 Roo.data.Record.COMMIT = 'commit';
9600
9601 Roo.data.Record.prototype = {
9602     /**
9603      * Readonly flag - true if this record has been modified.
9604      * @type Boolean
9605      */
9606     dirty : false,
9607     editing : false,
9608     error: null,
9609     modified: null,
9610
9611     // private
9612     join : function(store){
9613         this.store = store;
9614     },
9615
9616     /**
9617      * Set the named field to the specified value.
9618      * @param {String} name The name of the field to set.
9619      * @param {Object} value The value to set the field to.
9620      */
9621     set : function(name, value){
9622         if(this.data[name] == value){
9623             return;
9624         }
9625         this.dirty = true;
9626         if(!this.modified){
9627             this.modified = {};
9628         }
9629         if(typeof this.modified[name] == 'undefined'){
9630             this.modified[name] = this.data[name];
9631         }
9632         this.data[name] = value;
9633         if(!this.editing && this.store){
9634             this.store.afterEdit(this);
9635         }       
9636     },
9637
9638     /**
9639      * Get the value of the named field.
9640      * @param {String} name The name of the field to get the value of.
9641      * @return {Object} The value of the field.
9642      */
9643     get : function(name){
9644         return this.data[name]; 
9645     },
9646
9647     // private
9648     beginEdit : function(){
9649         this.editing = true;
9650         this.modified = {}; 
9651     },
9652
9653     // private
9654     cancelEdit : function(){
9655         this.editing = false;
9656         delete this.modified;
9657     },
9658
9659     // private
9660     endEdit : function(){
9661         this.editing = false;
9662         if(this.dirty && this.store){
9663             this.store.afterEdit(this);
9664         }
9665     },
9666
9667     /**
9668      * Usually called by the {@link Roo.data.Store} which owns the Record.
9669      * Rejects all changes made to the Record since either creation, or the last commit operation.
9670      * Modified fields are reverted to their original values.
9671      * <p>
9672      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9673      * of reject operations.
9674      */
9675     reject : function(){
9676         var m = this.modified;
9677         for(var n in m){
9678             if(typeof m[n] != "function"){
9679                 this.data[n] = m[n];
9680             }
9681         }
9682         this.dirty = false;
9683         delete this.modified;
9684         this.editing = false;
9685         if(this.store){
9686             this.store.afterReject(this);
9687         }
9688     },
9689
9690     /**
9691      * Usually called by the {@link Roo.data.Store} which owns the Record.
9692      * Commits all changes made to the Record since either creation, or the last commit operation.
9693      * <p>
9694      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9695      * of commit operations.
9696      */
9697     commit : function(){
9698         this.dirty = false;
9699         delete this.modified;
9700         this.editing = false;
9701         if(this.store){
9702             this.store.afterCommit(this);
9703         }
9704     },
9705
9706     // private
9707     hasError : function(){
9708         return this.error != null;
9709     },
9710
9711     // private
9712     clearError : function(){
9713         this.error = null;
9714     },
9715
9716     /**
9717      * Creates a copy of this record.
9718      * @param {String} id (optional) A new record id if you don't want to use this record's id
9719      * @return {Record}
9720      */
9721     copy : function(newId) {
9722         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9723     }
9724 };/*
9725  * Based on:
9726  * Ext JS Library 1.1.1
9727  * Copyright(c) 2006-2007, Ext JS, LLC.
9728  *
9729  * Originally Released Under LGPL - original licence link has changed is not relivant.
9730  *
9731  * Fork - LGPL
9732  * <script type="text/javascript">
9733  */
9734
9735
9736
9737 /**
9738  * @class Roo.data.Store
9739  * @extends Roo.util.Observable
9740  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9741  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9742  * <p>
9743  * 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
9744  * has no knowledge of the format of the data returned by the Proxy.<br>
9745  * <p>
9746  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9747  * instances from the data object. These records are cached and made available through accessor functions.
9748  * @constructor
9749  * Creates a new Store.
9750  * @param {Object} config A config object containing the objects needed for the Store to access data,
9751  * and read the data into Records.
9752  */
9753 Roo.data.Store = function(config){
9754     this.data = new Roo.util.MixedCollection(false);
9755     this.data.getKey = function(o){
9756         return o.id;
9757     };
9758     this.baseParams = {};
9759     // private
9760     this.paramNames = {
9761         "start" : "start",
9762         "limit" : "limit",
9763         "sort" : "sort",
9764         "dir" : "dir",
9765         "multisort" : "_multisort"
9766     };
9767
9768     if(config && config.data){
9769         this.inlineData = config.data;
9770         delete config.data;
9771     }
9772
9773     Roo.apply(this, config);
9774     
9775     if(this.reader){ // reader passed
9776         this.reader = Roo.factory(this.reader, Roo.data);
9777         this.reader.xmodule = this.xmodule || false;
9778         if(!this.recordType){
9779             this.recordType = this.reader.recordType;
9780         }
9781         if(this.reader.onMetaChange){
9782             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9783         }
9784     }
9785
9786     if(this.recordType){
9787         this.fields = this.recordType.prototype.fields;
9788     }
9789     this.modified = [];
9790
9791     this.addEvents({
9792         /**
9793          * @event datachanged
9794          * Fires when the data cache has changed, and a widget which is using this Store
9795          * as a Record cache should refresh its view.
9796          * @param {Store} this
9797          */
9798         datachanged : true,
9799         /**
9800          * @event metachange
9801          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9802          * @param {Store} this
9803          * @param {Object} meta The JSON metadata
9804          */
9805         metachange : true,
9806         /**
9807          * @event add
9808          * Fires when Records have been added to the Store
9809          * @param {Store} this
9810          * @param {Roo.data.Record[]} records The array of Records added
9811          * @param {Number} index The index at which the record(s) were added
9812          */
9813         add : true,
9814         /**
9815          * @event remove
9816          * Fires when a Record has been removed from the Store
9817          * @param {Store} this
9818          * @param {Roo.data.Record} record The Record that was removed
9819          * @param {Number} index The index at which the record was removed
9820          */
9821         remove : true,
9822         /**
9823          * @event update
9824          * Fires when a Record has been updated
9825          * @param {Store} this
9826          * @param {Roo.data.Record} record The Record that was updated
9827          * @param {String} operation The update operation being performed.  Value may be one of:
9828          * <pre><code>
9829  Roo.data.Record.EDIT
9830  Roo.data.Record.REJECT
9831  Roo.data.Record.COMMIT
9832          * </code></pre>
9833          */
9834         update : true,
9835         /**
9836          * @event clear
9837          * Fires when the data cache has been cleared.
9838          * @param {Store} this
9839          */
9840         clear : true,
9841         /**
9842          * @event beforeload
9843          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9844          * the load action will be canceled.
9845          * @param {Store} this
9846          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9847          */
9848         beforeload : true,
9849         /**
9850          * @event beforeloadadd
9851          * Fires after a new set of Records has been loaded.
9852          * @param {Store} this
9853          * @param {Roo.data.Record[]} records The Records that were loaded
9854          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9855          */
9856         beforeloadadd : true,
9857         /**
9858          * @event load
9859          * Fires after a new set of Records has been loaded, before they are added to the store.
9860          * @param {Store} this
9861          * @param {Roo.data.Record[]} records The Records that were loaded
9862          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9863          * @params {Object} return from reader
9864          */
9865         load : true,
9866         /**
9867          * @event loadexception
9868          * Fires if an exception occurs in the Proxy during loading.
9869          * Called with the signature of the Proxy's "loadexception" event.
9870          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9871          * 
9872          * @param {Proxy} 
9873          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9874          * @param {Object} load options 
9875          * @param {Object} jsonData from your request (normally this contains the Exception)
9876          */
9877         loadexception : true
9878     });
9879     
9880     if(this.proxy){
9881         this.proxy = Roo.factory(this.proxy, Roo.data);
9882         this.proxy.xmodule = this.xmodule || false;
9883         this.relayEvents(this.proxy,  ["loadexception"]);
9884     }
9885     this.sortToggle = {};
9886     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9887
9888     Roo.data.Store.superclass.constructor.call(this);
9889
9890     if(this.inlineData){
9891         this.loadData(this.inlineData);
9892         delete this.inlineData;
9893     }
9894 };
9895
9896 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9897      /**
9898     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9899     * without a remote query - used by combo/forms at present.
9900     */
9901     
9902     /**
9903     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9904     */
9905     /**
9906     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9907     */
9908     /**
9909     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9910     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9911     */
9912     /**
9913     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9914     * on any HTTP request
9915     */
9916     /**
9917     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9918     */
9919     /**
9920     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9921     */
9922     multiSort: false,
9923     /**
9924     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9925     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9926     */
9927     remoteSort : false,
9928
9929     /**
9930     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9931      * loaded or when a record is removed. (defaults to false).
9932     */
9933     pruneModifiedRecords : false,
9934
9935     // private
9936     lastOptions : null,
9937
9938     /**
9939      * Add Records to the Store and fires the add event.
9940      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9941      */
9942     add : function(records){
9943         records = [].concat(records);
9944         for(var i = 0, len = records.length; i < len; i++){
9945             records[i].join(this);
9946         }
9947         var index = this.data.length;
9948         this.data.addAll(records);
9949         this.fireEvent("add", this, records, index);
9950     },
9951
9952     /**
9953      * Remove a Record from the Store and fires the remove event.
9954      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9955      */
9956     remove : function(record){
9957         var index = this.data.indexOf(record);
9958         this.data.removeAt(index);
9959         if(this.pruneModifiedRecords){
9960             this.modified.remove(record);
9961         }
9962         this.fireEvent("remove", this, record, index);
9963     },
9964
9965     /**
9966      * Remove all Records from the Store and fires the clear event.
9967      */
9968     removeAll : function(){
9969         this.data.clear();
9970         if(this.pruneModifiedRecords){
9971             this.modified = [];
9972         }
9973         this.fireEvent("clear", this);
9974     },
9975
9976     /**
9977      * Inserts Records to the Store at the given index and fires the add event.
9978      * @param {Number} index The start index at which to insert the passed Records.
9979      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9980      */
9981     insert : function(index, records){
9982         records = [].concat(records);
9983         for(var i = 0, len = records.length; i < len; i++){
9984             this.data.insert(index, records[i]);
9985             records[i].join(this);
9986         }
9987         this.fireEvent("add", this, records, index);
9988     },
9989
9990     /**
9991      * Get the index within the cache of the passed Record.
9992      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9993      * @return {Number} The index of the passed Record. Returns -1 if not found.
9994      */
9995     indexOf : function(record){
9996         return this.data.indexOf(record);
9997     },
9998
9999     /**
10000      * Get the index within the cache of the Record with the passed id.
10001      * @param {String} id The id of the Record to find.
10002      * @return {Number} The index of the Record. Returns -1 if not found.
10003      */
10004     indexOfId : function(id){
10005         return this.data.indexOfKey(id);
10006     },
10007
10008     /**
10009      * Get the Record with the specified id.
10010      * @param {String} id The id of the Record to find.
10011      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10012      */
10013     getById : function(id){
10014         return this.data.key(id);
10015     },
10016
10017     /**
10018      * Get the Record at the specified index.
10019      * @param {Number} index The index of the Record to find.
10020      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10021      */
10022     getAt : function(index){
10023         return this.data.itemAt(index);
10024     },
10025
10026     /**
10027      * Returns a range of Records between specified indices.
10028      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10029      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10030      * @return {Roo.data.Record[]} An array of Records
10031      */
10032     getRange : function(start, end){
10033         return this.data.getRange(start, end);
10034     },
10035
10036     // private
10037     storeOptions : function(o){
10038         o = Roo.apply({}, o);
10039         delete o.callback;
10040         delete o.scope;
10041         this.lastOptions = o;
10042     },
10043
10044     /**
10045      * Loads the Record cache from the configured Proxy using the configured Reader.
10046      * <p>
10047      * If using remote paging, then the first load call must specify the <em>start</em>
10048      * and <em>limit</em> properties in the options.params property to establish the initial
10049      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10050      * <p>
10051      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10052      * and this call will return before the new data has been loaded. Perform any post-processing
10053      * in a callback function, or in a "load" event handler.</strong>
10054      * <p>
10055      * @param {Object} options An object containing properties which control loading options:<ul>
10056      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10057      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10058      * passed the following arguments:<ul>
10059      * <li>r : Roo.data.Record[]</li>
10060      * <li>options: Options object from the load call</li>
10061      * <li>success: Boolean success indicator</li></ul></li>
10062      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10063      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10064      * </ul>
10065      */
10066     load : function(options){
10067         options = options || {};
10068         if(this.fireEvent("beforeload", this, options) !== false){
10069             this.storeOptions(options);
10070             var p = Roo.apply(options.params || {}, this.baseParams);
10071             // if meta was not loaded from remote source.. try requesting it.
10072             if (!this.reader.metaFromRemote) {
10073                 p._requestMeta = 1;
10074             }
10075             if(this.sortInfo && this.remoteSort){
10076                 var pn = this.paramNames;
10077                 p[pn["sort"]] = this.sortInfo.field;
10078                 p[pn["dir"]] = this.sortInfo.direction;
10079             }
10080             if (this.multiSort) {
10081                 var pn = this.paramNames;
10082                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10083             }
10084             
10085             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10086         }
10087     },
10088
10089     /**
10090      * Reloads the Record cache from the configured Proxy using the configured Reader and
10091      * the options from the last load operation performed.
10092      * @param {Object} options (optional) An object containing properties which may override the options
10093      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10094      * the most recently used options are reused).
10095      */
10096     reload : function(options){
10097         this.load(Roo.applyIf(options||{}, this.lastOptions));
10098     },
10099
10100     // private
10101     // Called as a callback by the Reader during a load operation.
10102     loadRecords : function(o, options, success){
10103         if(!o || success === false){
10104             if(success !== false){
10105                 this.fireEvent("load", this, [], options, o);
10106             }
10107             if(options.callback){
10108                 options.callback.call(options.scope || this, [], options, false);
10109             }
10110             return;
10111         }
10112         // if data returned failure - throw an exception.
10113         if (o.success === false) {
10114             // show a message if no listener is registered.
10115             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10116                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10117             }
10118             // loadmask wil be hooked into this..
10119             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10120             return;
10121         }
10122         var r = o.records, t = o.totalRecords || r.length;
10123         
10124         this.fireEvent("beforeloadadd", this, r, options, o);
10125         
10126         if(!options || options.add !== true){
10127             if(this.pruneModifiedRecords){
10128                 this.modified = [];
10129             }
10130             for(var i = 0, len = r.length; i < len; i++){
10131                 r[i].join(this);
10132             }
10133             if(this.snapshot){
10134                 this.data = this.snapshot;
10135                 delete this.snapshot;
10136             }
10137             this.data.clear();
10138             this.data.addAll(r);
10139             this.totalLength = t;
10140             this.applySort();
10141             this.fireEvent("datachanged", this);
10142         }else{
10143             this.totalLength = Math.max(t, this.data.length+r.length);
10144             this.add(r);
10145         }
10146         this.fireEvent("load", this, r, options, o);
10147         if(options.callback){
10148             options.callback.call(options.scope || this, r, options, true);
10149         }
10150     },
10151
10152
10153     /**
10154      * Loads data from a passed data block. A Reader which understands the format of the data
10155      * must have been configured in the constructor.
10156      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10157      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10158      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10159      */
10160     loadData : function(o, append){
10161         var r = this.reader.readRecords(o);
10162         this.loadRecords(r, {add: append}, true);
10163     },
10164
10165     /**
10166      * Gets the number of cached records.
10167      * <p>
10168      * <em>If using paging, this may not be the total size of the dataset. If the data object
10169      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10170      * the data set size</em>
10171      */
10172     getCount : function(){
10173         return this.data.length || 0;
10174     },
10175
10176     /**
10177      * Gets the total number of records in the dataset as returned by the server.
10178      * <p>
10179      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10180      * the dataset size</em>
10181      */
10182     getTotalCount : function(){
10183         return this.totalLength || 0;
10184     },
10185
10186     /**
10187      * Returns the sort state of the Store as an object with two properties:
10188      * <pre><code>
10189  field {String} The name of the field by which the Records are sorted
10190  direction {String} The sort order, "ASC" or "DESC"
10191      * </code></pre>
10192      */
10193     getSortState : function(){
10194         return this.sortInfo;
10195     },
10196
10197     // private
10198     applySort : function(){
10199         if(this.sortInfo && !this.remoteSort){
10200             var s = this.sortInfo, f = s.field;
10201             var st = this.fields.get(f).sortType;
10202             var fn = function(r1, r2){
10203                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10204                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10205             };
10206             this.data.sort(s.direction, fn);
10207             if(this.snapshot && this.snapshot != this.data){
10208                 this.snapshot.sort(s.direction, fn);
10209             }
10210         }
10211     },
10212
10213     /**
10214      * Sets the default sort column and order to be used by the next load operation.
10215      * @param {String} fieldName The name of the field to sort by.
10216      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10217      */
10218     setDefaultSort : function(field, dir){
10219         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10220     },
10221
10222     /**
10223      * Sort the Records.
10224      * If remote sorting is used, the sort is performed on the server, and the cache is
10225      * reloaded. If local sorting is used, the cache is sorted internally.
10226      * @param {String} fieldName The name of the field to sort by.
10227      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10228      */
10229     sort : function(fieldName, dir){
10230         var f = this.fields.get(fieldName);
10231         if(!dir){
10232             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10233             
10234             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10235                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10236             }else{
10237                 dir = f.sortDir;
10238             }
10239         }
10240         this.sortToggle[f.name] = dir;
10241         this.sortInfo = {field: f.name, direction: dir};
10242         if(!this.remoteSort){
10243             this.applySort();
10244             this.fireEvent("datachanged", this);
10245         }else{
10246             this.load(this.lastOptions);
10247         }
10248     },
10249
10250     /**
10251      * Calls the specified function for each of the Records in the cache.
10252      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10253      * Returning <em>false</em> aborts and exits the iteration.
10254      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10255      */
10256     each : function(fn, scope){
10257         this.data.each(fn, scope);
10258     },
10259
10260     /**
10261      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10262      * (e.g., during paging).
10263      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10264      */
10265     getModifiedRecords : function(){
10266         return this.modified;
10267     },
10268
10269     // private
10270     createFilterFn : function(property, value, anyMatch){
10271         if(!value.exec){ // not a regex
10272             value = String(value);
10273             if(value.length == 0){
10274                 return false;
10275             }
10276             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10277         }
10278         return function(r){
10279             return value.test(r.data[property]);
10280         };
10281     },
10282
10283     /**
10284      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10285      * @param {String} property A field on your records
10286      * @param {Number} start The record index to start at (defaults to 0)
10287      * @param {Number} end The last record index to include (defaults to length - 1)
10288      * @return {Number} The sum
10289      */
10290     sum : function(property, start, end){
10291         var rs = this.data.items, v = 0;
10292         start = start || 0;
10293         end = (end || end === 0) ? end : rs.length-1;
10294
10295         for(var i = start; i <= end; i++){
10296             v += (rs[i].data[property] || 0);
10297         }
10298         return v;
10299     },
10300
10301     /**
10302      * Filter the records by a specified property.
10303      * @param {String} field A field on your records
10304      * @param {String/RegExp} value Either a string that the field
10305      * should start with or a RegExp to test against the field
10306      * @param {Boolean} anyMatch True to match any part not just the beginning
10307      */
10308     filter : function(property, value, anyMatch){
10309         var fn = this.createFilterFn(property, value, anyMatch);
10310         return fn ? this.filterBy(fn) : this.clearFilter();
10311     },
10312
10313     /**
10314      * Filter by a function. The specified function will be called with each
10315      * record in this data source. If the function returns true the record is included,
10316      * otherwise it is filtered.
10317      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10318      * @param {Object} scope (optional) The scope of the function (defaults to this)
10319      */
10320     filterBy : function(fn, scope){
10321         this.snapshot = this.snapshot || this.data;
10322         this.data = this.queryBy(fn, scope||this);
10323         this.fireEvent("datachanged", this);
10324     },
10325
10326     /**
10327      * Query the records by a specified property.
10328      * @param {String} field A field on your records
10329      * @param {String/RegExp} value Either a string that the field
10330      * should start with or a RegExp to test against the field
10331      * @param {Boolean} anyMatch True to match any part not just the beginning
10332      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10333      */
10334     query : function(property, value, anyMatch){
10335         var fn = this.createFilterFn(property, value, anyMatch);
10336         return fn ? this.queryBy(fn) : this.data.clone();
10337     },
10338
10339     /**
10340      * Query by a function. The specified function will be called with each
10341      * record in this data source. If the function returns true the record is included
10342      * in the results.
10343      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10344      * @param {Object} scope (optional) The scope of the function (defaults to this)
10345       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10346      **/
10347     queryBy : function(fn, scope){
10348         var data = this.snapshot || this.data;
10349         return data.filterBy(fn, scope||this);
10350     },
10351
10352     /**
10353      * Collects unique values for a particular dataIndex from this store.
10354      * @param {String} dataIndex The property to collect
10355      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10356      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10357      * @return {Array} An array of the unique values
10358      **/
10359     collect : function(dataIndex, allowNull, bypassFilter){
10360         var d = (bypassFilter === true && this.snapshot) ?
10361                 this.snapshot.items : this.data.items;
10362         var v, sv, r = [], l = {};
10363         for(var i = 0, len = d.length; i < len; i++){
10364             v = d[i].data[dataIndex];
10365             sv = String(v);
10366             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10367                 l[sv] = true;
10368                 r[r.length] = v;
10369             }
10370         }
10371         return r;
10372     },
10373
10374     /**
10375      * Revert to a view of the Record cache with no filtering applied.
10376      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10377      */
10378     clearFilter : function(suppressEvent){
10379         if(this.snapshot && this.snapshot != this.data){
10380             this.data = this.snapshot;
10381             delete this.snapshot;
10382             if(suppressEvent !== true){
10383                 this.fireEvent("datachanged", this);
10384             }
10385         }
10386     },
10387
10388     // private
10389     afterEdit : function(record){
10390         if(this.modified.indexOf(record) == -1){
10391             this.modified.push(record);
10392         }
10393         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10394     },
10395     
10396     // private
10397     afterReject : function(record){
10398         this.modified.remove(record);
10399         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10400     },
10401
10402     // private
10403     afterCommit : function(record){
10404         this.modified.remove(record);
10405         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10406     },
10407
10408     /**
10409      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10410      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10411      */
10412     commitChanges : function(){
10413         var m = this.modified.slice(0);
10414         this.modified = [];
10415         for(var i = 0, len = m.length; i < len; i++){
10416             m[i].commit();
10417         }
10418     },
10419
10420     /**
10421      * Cancel outstanding changes on all changed records.
10422      */
10423     rejectChanges : function(){
10424         var m = this.modified.slice(0);
10425         this.modified = [];
10426         for(var i = 0, len = m.length; i < len; i++){
10427             m[i].reject();
10428         }
10429     },
10430
10431     onMetaChange : function(meta, rtype, o){
10432         this.recordType = rtype;
10433         this.fields = rtype.prototype.fields;
10434         delete this.snapshot;
10435         this.sortInfo = meta.sortInfo || this.sortInfo;
10436         this.modified = [];
10437         this.fireEvent('metachange', this, this.reader.meta);
10438     },
10439     
10440     moveIndex : function(data, type)
10441     {
10442         var index = this.indexOf(data);
10443         
10444         var newIndex = index + type;
10445         
10446         this.remove(data);
10447         
10448         this.insert(newIndex, data);
10449         
10450     }
10451 });/*
10452  * Based on:
10453  * Ext JS Library 1.1.1
10454  * Copyright(c) 2006-2007, Ext JS, LLC.
10455  *
10456  * Originally Released Under LGPL - original licence link has changed is not relivant.
10457  *
10458  * Fork - LGPL
10459  * <script type="text/javascript">
10460  */
10461
10462 /**
10463  * @class Roo.data.SimpleStore
10464  * @extends Roo.data.Store
10465  * Small helper class to make creating Stores from Array data easier.
10466  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10467  * @cfg {Array} fields An array of field definition objects, or field name strings.
10468  * @cfg {Array} data The multi-dimensional array of data
10469  * @constructor
10470  * @param {Object} config
10471  */
10472 Roo.data.SimpleStore = function(config){
10473     Roo.data.SimpleStore.superclass.constructor.call(this, {
10474         isLocal : true,
10475         reader: new Roo.data.ArrayReader({
10476                 id: config.id
10477             },
10478             Roo.data.Record.create(config.fields)
10479         ),
10480         proxy : new Roo.data.MemoryProxy(config.data)
10481     });
10482     this.load();
10483 };
10484 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10485  * Based on:
10486  * Ext JS Library 1.1.1
10487  * Copyright(c) 2006-2007, Ext JS, LLC.
10488  *
10489  * Originally Released Under LGPL - original licence link has changed is not relivant.
10490  *
10491  * Fork - LGPL
10492  * <script type="text/javascript">
10493  */
10494
10495 /**
10496 /**
10497  * @extends Roo.data.Store
10498  * @class Roo.data.JsonStore
10499  * Small helper class to make creating Stores for JSON data easier. <br/>
10500 <pre><code>
10501 var store = new Roo.data.JsonStore({
10502     url: 'get-images.php',
10503     root: 'images',
10504     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10505 });
10506 </code></pre>
10507  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10508  * JsonReader and HttpProxy (unless inline data is provided).</b>
10509  * @cfg {Array} fields An array of field definition objects, or field name strings.
10510  * @constructor
10511  * @param {Object} config
10512  */
10513 Roo.data.JsonStore = function(c){
10514     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10515         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10516         reader: new Roo.data.JsonReader(c, c.fields)
10517     }));
10518 };
10519 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10520  * Based on:
10521  * Ext JS Library 1.1.1
10522  * Copyright(c) 2006-2007, Ext JS, LLC.
10523  *
10524  * Originally Released Under LGPL - original licence link has changed is not relivant.
10525  *
10526  * Fork - LGPL
10527  * <script type="text/javascript">
10528  */
10529
10530  
10531 Roo.data.Field = function(config){
10532     if(typeof config == "string"){
10533         config = {name: config};
10534     }
10535     Roo.apply(this, config);
10536     
10537     if(!this.type){
10538         this.type = "auto";
10539     }
10540     
10541     var st = Roo.data.SortTypes;
10542     // named sortTypes are supported, here we look them up
10543     if(typeof this.sortType == "string"){
10544         this.sortType = st[this.sortType];
10545     }
10546     
10547     // set default sortType for strings and dates
10548     if(!this.sortType){
10549         switch(this.type){
10550             case "string":
10551                 this.sortType = st.asUCString;
10552                 break;
10553             case "date":
10554                 this.sortType = st.asDate;
10555                 break;
10556             default:
10557                 this.sortType = st.none;
10558         }
10559     }
10560
10561     // define once
10562     var stripRe = /[\$,%]/g;
10563
10564     // prebuilt conversion function for this field, instead of
10565     // switching every time we're reading a value
10566     if(!this.convert){
10567         var cv, dateFormat = this.dateFormat;
10568         switch(this.type){
10569             case "":
10570             case "auto":
10571             case undefined:
10572                 cv = function(v){ return v; };
10573                 break;
10574             case "string":
10575                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10576                 break;
10577             case "int":
10578                 cv = function(v){
10579                     return v !== undefined && v !== null && v !== '' ?
10580                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10581                     };
10582                 break;
10583             case "float":
10584                 cv = function(v){
10585                     return v !== undefined && v !== null && v !== '' ?
10586                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10587                     };
10588                 break;
10589             case "bool":
10590             case "boolean":
10591                 cv = function(v){ return v === true || v === "true" || v == 1; };
10592                 break;
10593             case "date":
10594                 cv = function(v){
10595                     if(!v){
10596                         return '';
10597                     }
10598                     if(v instanceof Date){
10599                         return v;
10600                     }
10601                     if(dateFormat){
10602                         if(dateFormat == "timestamp"){
10603                             return new Date(v*1000);
10604                         }
10605                         return Date.parseDate(v, dateFormat);
10606                     }
10607                     var parsed = Date.parse(v);
10608                     return parsed ? new Date(parsed) : null;
10609                 };
10610              break;
10611             
10612         }
10613         this.convert = cv;
10614     }
10615 };
10616
10617 Roo.data.Field.prototype = {
10618     dateFormat: null,
10619     defaultValue: "",
10620     mapping: null,
10621     sortType : null,
10622     sortDir : "ASC"
10623 };/*
10624  * Based on:
10625  * Ext JS Library 1.1.1
10626  * Copyright(c) 2006-2007, Ext JS, LLC.
10627  *
10628  * Originally Released Under LGPL - original licence link has changed is not relivant.
10629  *
10630  * Fork - LGPL
10631  * <script type="text/javascript">
10632  */
10633  
10634 // Base class for reading structured data from a data source.  This class is intended to be
10635 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10636
10637 /**
10638  * @class Roo.data.DataReader
10639  * Base class for reading structured data from a data source.  This class is intended to be
10640  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10641  */
10642
10643 Roo.data.DataReader = function(meta, recordType){
10644     
10645     this.meta = meta;
10646     
10647     this.recordType = recordType instanceof Array ? 
10648         Roo.data.Record.create(recordType) : recordType;
10649 };
10650
10651 Roo.data.DataReader.prototype = {
10652      /**
10653      * Create an empty record
10654      * @param {Object} data (optional) - overlay some values
10655      * @return {Roo.data.Record} record created.
10656      */
10657     newRow :  function(d) {
10658         var da =  {};
10659         this.recordType.prototype.fields.each(function(c) {
10660             switch( c.type) {
10661                 case 'int' : da[c.name] = 0; break;
10662                 case 'date' : da[c.name] = new Date(); break;
10663                 case 'float' : da[c.name] = 0.0; break;
10664                 case 'boolean' : da[c.name] = false; break;
10665                 default : da[c.name] = ""; break;
10666             }
10667             
10668         });
10669         return new this.recordType(Roo.apply(da, d));
10670     }
10671     
10672 };/*
10673  * Based on:
10674  * Ext JS Library 1.1.1
10675  * Copyright(c) 2006-2007, Ext JS, LLC.
10676  *
10677  * Originally Released Under LGPL - original licence link has changed is not relivant.
10678  *
10679  * Fork - LGPL
10680  * <script type="text/javascript">
10681  */
10682
10683 /**
10684  * @class Roo.data.DataProxy
10685  * @extends Roo.data.Observable
10686  * This class is an abstract base class for implementations which provide retrieval of
10687  * unformatted data objects.<br>
10688  * <p>
10689  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10690  * (of the appropriate type which knows how to parse the data object) to provide a block of
10691  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10692  * <p>
10693  * Custom implementations must implement the load method as described in
10694  * {@link Roo.data.HttpProxy#load}.
10695  */
10696 Roo.data.DataProxy = function(){
10697     this.addEvents({
10698         /**
10699          * @event beforeload
10700          * Fires before a network request is made to retrieve a data object.
10701          * @param {Object} This DataProxy object.
10702          * @param {Object} params The params parameter to the load function.
10703          */
10704         beforeload : true,
10705         /**
10706          * @event load
10707          * Fires before the load method's callback is called.
10708          * @param {Object} This DataProxy object.
10709          * @param {Object} o The data object.
10710          * @param {Object} arg The callback argument object passed to the load function.
10711          */
10712         load : true,
10713         /**
10714          * @event loadexception
10715          * Fires if an Exception occurs during data retrieval.
10716          * @param {Object} This DataProxy object.
10717          * @param {Object} o The data object.
10718          * @param {Object} arg The callback argument object passed to the load function.
10719          * @param {Object} e The Exception.
10720          */
10721         loadexception : true
10722     });
10723     Roo.data.DataProxy.superclass.constructor.call(this);
10724 };
10725
10726 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10727
10728     /**
10729      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10730      */
10731 /*
10732  * Based on:
10733  * Ext JS Library 1.1.1
10734  * Copyright(c) 2006-2007, Ext JS, LLC.
10735  *
10736  * Originally Released Under LGPL - original licence link has changed is not relivant.
10737  *
10738  * Fork - LGPL
10739  * <script type="text/javascript">
10740  */
10741 /**
10742  * @class Roo.data.MemoryProxy
10743  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10744  * to the Reader when its load method is called.
10745  * @constructor
10746  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10747  */
10748 Roo.data.MemoryProxy = function(data){
10749     if (data.data) {
10750         data = data.data;
10751     }
10752     Roo.data.MemoryProxy.superclass.constructor.call(this);
10753     this.data = data;
10754 };
10755
10756 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10757     /**
10758      * Load data from the requested source (in this case an in-memory
10759      * data object passed to the constructor), read the data object into
10760      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10761      * process that block using the passed callback.
10762      * @param {Object} params This parameter is not used by the MemoryProxy class.
10763      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10764      * object into a block of Roo.data.Records.
10765      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10766      * The function must be passed <ul>
10767      * <li>The Record block object</li>
10768      * <li>The "arg" argument from the load function</li>
10769      * <li>A boolean success indicator</li>
10770      * </ul>
10771      * @param {Object} scope The scope in which to call the callback
10772      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10773      */
10774     load : function(params, reader, callback, scope, arg){
10775         params = params || {};
10776         var result;
10777         try {
10778             result = reader.readRecords(this.data);
10779         }catch(e){
10780             this.fireEvent("loadexception", this, arg, null, e);
10781             callback.call(scope, null, arg, false);
10782             return;
10783         }
10784         callback.call(scope, result, arg, true);
10785     },
10786     
10787     // private
10788     update : function(params, records){
10789         
10790     }
10791 });/*
10792  * Based on:
10793  * Ext JS Library 1.1.1
10794  * Copyright(c) 2006-2007, Ext JS, LLC.
10795  *
10796  * Originally Released Under LGPL - original licence link has changed is not relivant.
10797  *
10798  * Fork - LGPL
10799  * <script type="text/javascript">
10800  */
10801 /**
10802  * @class Roo.data.HttpProxy
10803  * @extends Roo.data.DataProxy
10804  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10805  * configured to reference a certain URL.<br><br>
10806  * <p>
10807  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10808  * from which the running page was served.<br><br>
10809  * <p>
10810  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10811  * <p>
10812  * Be aware that to enable the browser to parse an XML document, the server must set
10813  * the Content-Type header in the HTTP response to "text/xml".
10814  * @constructor
10815  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10816  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10817  * will be used to make the request.
10818  */
10819 Roo.data.HttpProxy = function(conn){
10820     Roo.data.HttpProxy.superclass.constructor.call(this);
10821     // is conn a conn config or a real conn?
10822     this.conn = conn;
10823     this.useAjax = !conn || !conn.events;
10824   
10825 };
10826
10827 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10828     // thse are take from connection...
10829     
10830     /**
10831      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10832      */
10833     /**
10834      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10835      * extra parameters to each request made by this object. (defaults to undefined)
10836      */
10837     /**
10838      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10839      *  to each request made by this object. (defaults to undefined)
10840      */
10841     /**
10842      * @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)
10843      */
10844     /**
10845      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10846      */
10847      /**
10848      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10849      * @type Boolean
10850      */
10851   
10852
10853     /**
10854      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10855      * @type Boolean
10856      */
10857     /**
10858      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10859      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10860      * a finer-grained basis than the DataProxy events.
10861      */
10862     getConnection : function(){
10863         return this.useAjax ? Roo.Ajax : this.conn;
10864     },
10865
10866     /**
10867      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10868      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10869      * process that block using the passed callback.
10870      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10871      * for the request to the remote server.
10872      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10873      * object into a block of Roo.data.Records.
10874      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10875      * The function must be passed <ul>
10876      * <li>The Record block object</li>
10877      * <li>The "arg" argument from the load function</li>
10878      * <li>A boolean success indicator</li>
10879      * </ul>
10880      * @param {Object} scope The scope in which to call the callback
10881      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10882      */
10883     load : function(params, reader, callback, scope, arg){
10884         if(this.fireEvent("beforeload", this, params) !== false){
10885             var  o = {
10886                 params : params || {},
10887                 request: {
10888                     callback : callback,
10889                     scope : scope,
10890                     arg : arg
10891                 },
10892                 reader: reader,
10893                 callback : this.loadResponse,
10894                 scope: this
10895             };
10896             if(this.useAjax){
10897                 Roo.applyIf(o, this.conn);
10898                 if(this.activeRequest){
10899                     Roo.Ajax.abort(this.activeRequest);
10900                 }
10901                 this.activeRequest = Roo.Ajax.request(o);
10902             }else{
10903                 this.conn.request(o);
10904             }
10905         }else{
10906             callback.call(scope||this, null, arg, false);
10907         }
10908     },
10909
10910     // private
10911     loadResponse : function(o, success, response){
10912         delete this.activeRequest;
10913         if(!success){
10914             this.fireEvent("loadexception", this, o, response);
10915             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10916             return;
10917         }
10918         var result;
10919         try {
10920             result = o.reader.read(response);
10921         }catch(e){
10922             this.fireEvent("loadexception", this, o, response, e);
10923             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10924             return;
10925         }
10926         
10927         this.fireEvent("load", this, o, o.request.arg);
10928         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10929     },
10930
10931     // private
10932     update : function(dataSet){
10933
10934     },
10935
10936     // private
10937     updateResponse : function(dataSet){
10938
10939     }
10940 });/*
10941  * Based on:
10942  * Ext JS Library 1.1.1
10943  * Copyright(c) 2006-2007, Ext JS, LLC.
10944  *
10945  * Originally Released Under LGPL - original licence link has changed is not relivant.
10946  *
10947  * Fork - LGPL
10948  * <script type="text/javascript">
10949  */
10950
10951 /**
10952  * @class Roo.data.ScriptTagProxy
10953  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10954  * other than the originating domain of the running page.<br><br>
10955  * <p>
10956  * <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
10957  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10958  * <p>
10959  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10960  * source code that is used as the source inside a &lt;script> tag.<br><br>
10961  * <p>
10962  * In order for the browser to process the returned data, the server must wrap the data object
10963  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10964  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10965  * depending on whether the callback name was passed:
10966  * <p>
10967  * <pre><code>
10968 boolean scriptTag = false;
10969 String cb = request.getParameter("callback");
10970 if (cb != null) {
10971     scriptTag = true;
10972     response.setContentType("text/javascript");
10973 } else {
10974     response.setContentType("application/x-json");
10975 }
10976 Writer out = response.getWriter();
10977 if (scriptTag) {
10978     out.write(cb + "(");
10979 }
10980 out.print(dataBlock.toJsonString());
10981 if (scriptTag) {
10982     out.write(");");
10983 }
10984 </pre></code>
10985  *
10986  * @constructor
10987  * @param {Object} config A configuration object.
10988  */
10989 Roo.data.ScriptTagProxy = function(config){
10990     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10991     Roo.apply(this, config);
10992     this.head = document.getElementsByTagName("head")[0];
10993 };
10994
10995 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10996
10997 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10998     /**
10999      * @cfg {String} url The URL from which to request the data object.
11000      */
11001     /**
11002      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11003      */
11004     timeout : 30000,
11005     /**
11006      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11007      * the server the name of the callback function set up by the load call to process the returned data object.
11008      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11009      * javascript output which calls this named function passing the data object as its only parameter.
11010      */
11011     callbackParam : "callback",
11012     /**
11013      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11014      * name to the request.
11015      */
11016     nocache : true,
11017
11018     /**
11019      * Load data from the configured URL, read the data object into
11020      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11021      * process that block using the passed callback.
11022      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11023      * for the request to the remote server.
11024      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11025      * object into a block of Roo.data.Records.
11026      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11027      * The function must be passed <ul>
11028      * <li>The Record block object</li>
11029      * <li>The "arg" argument from the load function</li>
11030      * <li>A boolean success indicator</li>
11031      * </ul>
11032      * @param {Object} scope The scope in which to call the callback
11033      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11034      */
11035     load : function(params, reader, callback, scope, arg){
11036         if(this.fireEvent("beforeload", this, params) !== false){
11037
11038             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11039
11040             var url = this.url;
11041             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11042             if(this.nocache){
11043                 url += "&_dc=" + (new Date().getTime());
11044             }
11045             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11046             var trans = {
11047                 id : transId,
11048                 cb : "stcCallback"+transId,
11049                 scriptId : "stcScript"+transId,
11050                 params : params,
11051                 arg : arg,
11052                 url : url,
11053                 callback : callback,
11054                 scope : scope,
11055                 reader : reader
11056             };
11057             var conn = this;
11058
11059             window[trans.cb] = function(o){
11060                 conn.handleResponse(o, trans);
11061             };
11062
11063             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11064
11065             if(this.autoAbort !== false){
11066                 this.abort();
11067             }
11068
11069             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11070
11071             var script = document.createElement("script");
11072             script.setAttribute("src", url);
11073             script.setAttribute("type", "text/javascript");
11074             script.setAttribute("id", trans.scriptId);
11075             this.head.appendChild(script);
11076
11077             this.trans = trans;
11078         }else{
11079             callback.call(scope||this, null, arg, false);
11080         }
11081     },
11082
11083     // private
11084     isLoading : function(){
11085         return this.trans ? true : false;
11086     },
11087
11088     /**
11089      * Abort the current server request.
11090      */
11091     abort : function(){
11092         if(this.isLoading()){
11093             this.destroyTrans(this.trans);
11094         }
11095     },
11096
11097     // private
11098     destroyTrans : function(trans, isLoaded){
11099         this.head.removeChild(document.getElementById(trans.scriptId));
11100         clearTimeout(trans.timeoutId);
11101         if(isLoaded){
11102             window[trans.cb] = undefined;
11103             try{
11104                 delete window[trans.cb];
11105             }catch(e){}
11106         }else{
11107             // if hasn't been loaded, wait for load to remove it to prevent script error
11108             window[trans.cb] = function(){
11109                 window[trans.cb] = undefined;
11110                 try{
11111                     delete window[trans.cb];
11112                 }catch(e){}
11113             };
11114         }
11115     },
11116
11117     // private
11118     handleResponse : function(o, trans){
11119         this.trans = false;
11120         this.destroyTrans(trans, true);
11121         var result;
11122         try {
11123             result = trans.reader.readRecords(o);
11124         }catch(e){
11125             this.fireEvent("loadexception", this, o, trans.arg, e);
11126             trans.callback.call(trans.scope||window, null, trans.arg, false);
11127             return;
11128         }
11129         this.fireEvent("load", this, o, trans.arg);
11130         trans.callback.call(trans.scope||window, result, trans.arg, true);
11131     },
11132
11133     // private
11134     handleFailure : function(trans){
11135         this.trans = false;
11136         this.destroyTrans(trans, false);
11137         this.fireEvent("loadexception", this, null, trans.arg);
11138         trans.callback.call(trans.scope||window, null, trans.arg, false);
11139     }
11140 });/*
11141  * Based on:
11142  * Ext JS Library 1.1.1
11143  * Copyright(c) 2006-2007, Ext JS, LLC.
11144  *
11145  * Originally Released Under LGPL - original licence link has changed is not relivant.
11146  *
11147  * Fork - LGPL
11148  * <script type="text/javascript">
11149  */
11150
11151 /**
11152  * @class Roo.data.JsonReader
11153  * @extends Roo.data.DataReader
11154  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11155  * based on mappings in a provided Roo.data.Record constructor.
11156  * 
11157  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11158  * in the reply previously. 
11159  * 
11160  * <p>
11161  * Example code:
11162  * <pre><code>
11163 var RecordDef = Roo.data.Record.create([
11164     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11165     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11166 ]);
11167 var myReader = new Roo.data.JsonReader({
11168     totalProperty: "results",    // The property which contains the total dataset size (optional)
11169     root: "rows",                // The property which contains an Array of row objects
11170     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11171 }, RecordDef);
11172 </code></pre>
11173  * <p>
11174  * This would consume a JSON file like this:
11175  * <pre><code>
11176 { 'results': 2, 'rows': [
11177     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11178     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11179 }
11180 </code></pre>
11181  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11182  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11183  * paged from the remote server.
11184  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11185  * @cfg {String} root name of the property which contains the Array of row objects.
11186  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11187  * @cfg {Array} fields Array of field definition objects
11188  * @constructor
11189  * Create a new JsonReader
11190  * @param {Object} meta Metadata configuration options
11191  * @param {Object} recordType Either an Array of field definition objects,
11192  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11193  */
11194 Roo.data.JsonReader = function(meta, recordType){
11195     
11196     meta = meta || {};
11197     // set some defaults:
11198     Roo.applyIf(meta, {
11199         totalProperty: 'total',
11200         successProperty : 'success',
11201         root : 'data',
11202         id : 'id'
11203     });
11204     
11205     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11206 };
11207 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11208     
11209     /**
11210      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11211      * Used by Store query builder to append _requestMeta to params.
11212      * 
11213      */
11214     metaFromRemote : false,
11215     /**
11216      * This method is only used by a DataProxy which has retrieved data from a remote server.
11217      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11218      * @return {Object} data A data block which is used by an Roo.data.Store object as
11219      * a cache of Roo.data.Records.
11220      */
11221     read : function(response){
11222         var json = response.responseText;
11223        
11224         var o = /* eval:var:o */ eval("("+json+")");
11225         if(!o) {
11226             throw {message: "JsonReader.read: Json object not found"};
11227         }
11228         
11229         if(o.metaData){
11230             
11231             delete this.ef;
11232             this.metaFromRemote = true;
11233             this.meta = o.metaData;
11234             this.recordType = Roo.data.Record.create(o.metaData.fields);
11235             this.onMetaChange(this.meta, this.recordType, o);
11236         }
11237         return this.readRecords(o);
11238     },
11239
11240     // private function a store will implement
11241     onMetaChange : function(meta, recordType, o){
11242
11243     },
11244
11245     /**
11246          * @ignore
11247          */
11248     simpleAccess: function(obj, subsc) {
11249         return obj[subsc];
11250     },
11251
11252         /**
11253          * @ignore
11254          */
11255     getJsonAccessor: function(){
11256         var re = /[\[\.]/;
11257         return function(expr) {
11258             try {
11259                 return(re.test(expr))
11260                     ? new Function("obj", "return obj." + expr)
11261                     : function(obj){
11262                         return obj[expr];
11263                     };
11264             } catch(e){}
11265             return Roo.emptyFn;
11266         };
11267     }(),
11268
11269     /**
11270      * Create a data block containing Roo.data.Records from an XML document.
11271      * @param {Object} o An object which contains an Array of row objects in the property specified
11272      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11273      * which contains the total size of the dataset.
11274      * @return {Object} data A data block which is used by an Roo.data.Store object as
11275      * a cache of Roo.data.Records.
11276      */
11277     readRecords : function(o){
11278         /**
11279          * After any data loads, the raw JSON data is available for further custom processing.
11280          * @type Object
11281          */
11282         this.o = o;
11283         var s = this.meta, Record = this.recordType,
11284             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11285
11286 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11287         if (!this.ef) {
11288             if(s.totalProperty) {
11289                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11290                 }
11291                 if(s.successProperty) {
11292                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11293                 }
11294                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11295                 if (s.id) {
11296                         var g = this.getJsonAccessor(s.id);
11297                         this.getId = function(rec) {
11298                                 var r = g(rec);  
11299                                 return (r === undefined || r === "") ? null : r;
11300                         };
11301                 } else {
11302                         this.getId = function(){return null;};
11303                 }
11304             this.ef = [];
11305             for(var jj = 0; jj < fl; jj++){
11306                 f = fi[jj];
11307                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11308                 this.ef[jj] = this.getJsonAccessor(map);
11309             }
11310         }
11311
11312         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11313         if(s.totalProperty){
11314             var vt = parseInt(this.getTotal(o), 10);
11315             if(!isNaN(vt)){
11316                 totalRecords = vt;
11317             }
11318         }
11319         if(s.successProperty){
11320             var vs = this.getSuccess(o);
11321             if(vs === false || vs === 'false'){
11322                 success = false;
11323             }
11324         }
11325         var records = [];
11326         for(var i = 0; i < c; i++){
11327                 var n = root[i];
11328             var values = {};
11329             var id = this.getId(n);
11330             for(var j = 0; j < fl; j++){
11331                 f = fi[j];
11332             var v = this.ef[j](n);
11333             if (!f.convert) {
11334                 Roo.log('missing convert for ' + f.name);
11335                 Roo.log(f);
11336                 continue;
11337             }
11338             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11339             }
11340             var record = new Record(values, id);
11341             record.json = n;
11342             records[i] = record;
11343         }
11344         return {
11345             raw : o,
11346             success : success,
11347             records : records,
11348             totalRecords : totalRecords
11349         };
11350     }
11351 });/*
11352  * Based on:
11353  * Ext JS Library 1.1.1
11354  * Copyright(c) 2006-2007, Ext JS, LLC.
11355  *
11356  * Originally Released Under LGPL - original licence link has changed is not relivant.
11357  *
11358  * Fork - LGPL
11359  * <script type="text/javascript">
11360  */
11361
11362 /**
11363  * @class Roo.data.ArrayReader
11364  * @extends Roo.data.DataReader
11365  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11366  * Each element of that Array represents a row of data fields. The
11367  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11368  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11369  * <p>
11370  * Example code:.
11371  * <pre><code>
11372 var RecordDef = Roo.data.Record.create([
11373     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11374     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11375 ]);
11376 var myReader = new Roo.data.ArrayReader({
11377     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11378 }, RecordDef);
11379 </code></pre>
11380  * <p>
11381  * This would consume an Array like this:
11382  * <pre><code>
11383 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11384   </code></pre>
11385  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11386  * @constructor
11387  * Create a new JsonReader
11388  * @param {Object} meta Metadata configuration options.
11389  * @param {Object} recordType Either an Array of field definition objects
11390  * as specified to {@link Roo.data.Record#create},
11391  * or an {@link Roo.data.Record} object
11392  * created using {@link Roo.data.Record#create}.
11393  */
11394 Roo.data.ArrayReader = function(meta, recordType){
11395     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11396 };
11397
11398 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11399     /**
11400      * Create a data block containing Roo.data.Records from an XML document.
11401      * @param {Object} o An Array of row objects which represents the dataset.
11402      * @return {Object} data A data block which is used by an Roo.data.Store object as
11403      * a cache of Roo.data.Records.
11404      */
11405     readRecords : function(o){
11406         var sid = this.meta ? this.meta.id : null;
11407         var recordType = this.recordType, fields = recordType.prototype.fields;
11408         var records = [];
11409         var root = o;
11410             for(var i = 0; i < root.length; i++){
11411                     var n = root[i];
11412                 var values = {};
11413                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11414                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11415                 var f = fields.items[j];
11416                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11417                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11418                 v = f.convert(v);
11419                 values[f.name] = v;
11420             }
11421                 var record = new recordType(values, id);
11422                 record.json = n;
11423                 records[records.length] = record;
11424             }
11425             return {
11426                 records : records,
11427                 totalRecords : records.length
11428             };
11429     }
11430 });/*
11431  * - LGPL
11432  * * 
11433  */
11434
11435 /**
11436  * @class Roo.bootstrap.ComboBox
11437  * @extends Roo.bootstrap.TriggerField
11438  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11439  * @cfg {Boolean} append (true|false) default false
11440  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11441  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11442  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11443  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11444  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11445  * @cfg {Boolean} animate default true
11446  * @cfg {Boolean} emptyResultText only for touch device
11447  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11448  * @constructor
11449  * Create a new ComboBox.
11450  * @param {Object} config Configuration options
11451  */
11452 Roo.bootstrap.ComboBox = function(config){
11453     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11454     this.addEvents({
11455         /**
11456          * @event expand
11457          * Fires when the dropdown list is expanded
11458              * @param {Roo.bootstrap.ComboBox} combo This combo box
11459              */
11460         'expand' : true,
11461         /**
11462          * @event collapse
11463          * Fires when the dropdown list is collapsed
11464              * @param {Roo.bootstrap.ComboBox} combo This combo box
11465              */
11466         'collapse' : true,
11467         /**
11468          * @event beforeselect
11469          * Fires before a list item is selected. Return false to cancel the selection.
11470              * @param {Roo.bootstrap.ComboBox} combo This combo box
11471              * @param {Roo.data.Record} record The data record returned from the underlying store
11472              * @param {Number} index The index of the selected item in the dropdown list
11473              */
11474         'beforeselect' : true,
11475         /**
11476          * @event select
11477          * Fires when a list item is selected
11478              * @param {Roo.bootstrap.ComboBox} combo This combo box
11479              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11480              * @param {Number} index The index of the selected item in the dropdown list
11481              */
11482         'select' : true,
11483         /**
11484          * @event beforequery
11485          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11486          * The event object passed has these properties:
11487              * @param {Roo.bootstrap.ComboBox} combo This combo box
11488              * @param {String} query The query
11489              * @param {Boolean} forceAll true to force "all" query
11490              * @param {Boolean} cancel true to cancel the query
11491              * @param {Object} e The query event object
11492              */
11493         'beforequery': true,
11494          /**
11495          * @event add
11496          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11497              * @param {Roo.bootstrap.ComboBox} combo This combo box
11498              */
11499         'add' : true,
11500         /**
11501          * @event edit
11502          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11503              * @param {Roo.bootstrap.ComboBox} combo This combo box
11504              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11505              */
11506         'edit' : true,
11507         /**
11508          * @event remove
11509          * Fires when the remove value from the combobox array
11510              * @param {Roo.bootstrap.ComboBox} combo This combo box
11511              */
11512         'remove' : true,
11513         /**
11514          * @event specialfilter
11515          * Fires when specialfilter
11516             * @param {Roo.bootstrap.ComboBox} combo This combo box
11517             */
11518         'specialfilter' : true,
11519         /**
11520          * @event tick
11521          * Fires when tick the element
11522             * @param {Roo.bootstrap.ComboBox} combo This combo box
11523             */
11524         'tick' : true,
11525         /**
11526          * @event touchviewdisplay
11527          * Fires when touch view require special display (default is using displayField)
11528             * @param {Roo.bootstrap.ComboBox} combo This combo box
11529             * @param {Object} cfg set html .
11530             */
11531         'touchviewdisplay' : true
11532         
11533     });
11534     
11535     this.item = [];
11536     this.tickItems = [];
11537     
11538     this.selectedIndex = -1;
11539     if(this.mode == 'local'){
11540         if(config.queryDelay === undefined){
11541             this.queryDelay = 10;
11542         }
11543         if(config.minChars === undefined){
11544             this.minChars = 0;
11545         }
11546     }
11547 };
11548
11549 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11550      
11551     /**
11552      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11553      * rendering into an Roo.Editor, defaults to false)
11554      */
11555     /**
11556      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11557      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11558      */
11559     /**
11560      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11561      */
11562     /**
11563      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11564      * the dropdown list (defaults to undefined, with no header element)
11565      */
11566
11567      /**
11568      * @cfg {String/Roo.Template} tpl The template to use to render the output
11569      */
11570      
11571      /**
11572      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11573      */
11574     listWidth: undefined,
11575     /**
11576      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11577      * mode = 'remote' or 'text' if mode = 'local')
11578      */
11579     displayField: undefined,
11580     
11581     /**
11582      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11583      * mode = 'remote' or 'value' if mode = 'local'). 
11584      * Note: use of a valueField requires the user make a selection
11585      * in order for a value to be mapped.
11586      */
11587     valueField: undefined,
11588     
11589     
11590     /**
11591      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11592      * field's data value (defaults to the underlying DOM element's name)
11593      */
11594     hiddenName: undefined,
11595     /**
11596      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11597      */
11598     listClass: '',
11599     /**
11600      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11601      */
11602     selectedClass: 'active',
11603     
11604     /**
11605      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11606      */
11607     shadow:'sides',
11608     /**
11609      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11610      * anchor positions (defaults to 'tl-bl')
11611      */
11612     listAlign: 'tl-bl?',
11613     /**
11614      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11615      */
11616     maxHeight: 300,
11617     /**
11618      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11619      * query specified by the allQuery config option (defaults to 'query')
11620      */
11621     triggerAction: 'query',
11622     /**
11623      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11624      * (defaults to 4, does not apply if editable = false)
11625      */
11626     minChars : 4,
11627     /**
11628      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11629      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11630      */
11631     typeAhead: false,
11632     /**
11633      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11634      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11635      */
11636     queryDelay: 500,
11637     /**
11638      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11639      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11640      */
11641     pageSize: 0,
11642     /**
11643      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11644      * when editable = true (defaults to false)
11645      */
11646     selectOnFocus:false,
11647     /**
11648      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11649      */
11650     queryParam: 'query',
11651     /**
11652      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11653      * when mode = 'remote' (defaults to 'Loading...')
11654      */
11655     loadingText: 'Loading...',
11656     /**
11657      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11658      */
11659     resizable: false,
11660     /**
11661      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11662      */
11663     handleHeight : 8,
11664     /**
11665      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11666      * traditional select (defaults to true)
11667      */
11668     editable: true,
11669     /**
11670      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11671      */
11672     allQuery: '',
11673     /**
11674      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11675      */
11676     mode: 'remote',
11677     /**
11678      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11679      * listWidth has a higher value)
11680      */
11681     minListWidth : 70,
11682     /**
11683      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11684      * allow the user to set arbitrary text into the field (defaults to false)
11685      */
11686     forceSelection:false,
11687     /**
11688      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11689      * if typeAhead = true (defaults to 250)
11690      */
11691     typeAheadDelay : 250,
11692     /**
11693      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11694      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11695      */
11696     valueNotFoundText : undefined,
11697     /**
11698      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11699      */
11700     blockFocus : false,
11701     
11702     /**
11703      * @cfg {Boolean} disableClear Disable showing of clear button.
11704      */
11705     disableClear : false,
11706     /**
11707      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11708      */
11709     alwaysQuery : false,
11710     
11711     /**
11712      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11713      */
11714     multiple : false,
11715     
11716     /**
11717      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11718      */
11719     invalidClass : "has-warning",
11720     
11721     /**
11722      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11723      */
11724     validClass : "has-success",
11725     
11726     /**
11727      * @cfg {Boolean} specialFilter (true|false) special filter default false
11728      */
11729     specialFilter : false,
11730     
11731     /**
11732      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11733      */
11734     mobileTouchView : true,
11735     
11736     //private
11737     addicon : false,
11738     editicon: false,
11739     
11740     page: 0,
11741     hasQuery: false,
11742     append: false,
11743     loadNext: false,
11744     autoFocus : true,
11745     tickable : false,
11746     btnPosition : 'right',
11747     triggerList : true,
11748     showToggleBtn : true,
11749     animate : true,
11750     emptyResultText: 'Empty',
11751     triggerText : 'Select',
11752     
11753     // element that contains real text value.. (when hidden is used..)
11754     
11755     getAutoCreate : function()
11756     {
11757         var cfg = false;
11758         
11759         /*
11760          * Touch Devices
11761          */
11762         
11763         if(Roo.isTouch && this.mobileTouchView){
11764             cfg = this.getAutoCreateTouchView();
11765             return cfg;;
11766         }
11767         
11768         /*
11769          *  Normal ComboBox
11770          */
11771         if(!this.tickable){
11772             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11773             return cfg;
11774         }
11775         
11776         /*
11777          *  ComboBox with tickable selections
11778          */
11779              
11780         var align = this.labelAlign || this.parentLabelAlign();
11781         
11782         cfg = {
11783             cls : 'form-group roo-combobox-tickable' //input-group
11784         };
11785         
11786         var buttons = {
11787             tag : 'div',
11788             cls : 'tickable-buttons',
11789             cn : [
11790                 {
11791                     tag : 'button',
11792                     type : 'button',
11793                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11794                     html : this.triggerText
11795                 },
11796                 {
11797                     tag : 'button',
11798                     type : 'button',
11799                     name : 'ok',
11800                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11801                     html : 'Done'
11802                 },
11803                 {
11804                     tag : 'button',
11805                     type : 'button',
11806                     name : 'cancel',
11807                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11808                     html : 'Cancel'
11809                 }
11810             ]
11811         };
11812         
11813         if(this.editable){
11814             buttons.cn.unshift({
11815                 tag: 'input',
11816                 cls: 'select2-search-field-input'
11817             });
11818         }
11819         
11820         var _this = this;
11821         
11822         Roo.each(buttons.cn, function(c){
11823             if (_this.size) {
11824                 c.cls += ' btn-' + _this.size;
11825             }
11826
11827             if (_this.disabled) {
11828                 c.disabled = true;
11829             }
11830         });
11831         
11832         var box = {
11833             tag: 'div',
11834             cn: [
11835                 {
11836                     tag: 'input',
11837                     type : 'hidden',
11838                     cls: 'form-hidden-field'
11839                 },
11840                 {
11841                     tag: 'ul',
11842                     cls: 'select2-choices',
11843                     cn:[
11844                         {
11845                             tag: 'li',
11846                             cls: 'select2-search-field',
11847                             cn: [
11848
11849                                 buttons
11850                             ]
11851                         }
11852                     ]
11853                 }
11854             ]
11855         };
11856         
11857         var combobox = {
11858             cls: 'select2-container input-group select2-container-multi',
11859             cn: [
11860                 box
11861 //                {
11862 //                    tag: 'ul',
11863 //                    cls: 'typeahead typeahead-long dropdown-menu',
11864 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11865 //                }
11866             ]
11867         };
11868         
11869         if(this.hasFeedback && !this.allowBlank){
11870             
11871             var feedback = {
11872                 tag: 'span',
11873                 cls: 'glyphicon form-control-feedback'
11874             };
11875
11876             combobox.cn.push(feedback);
11877         }
11878         
11879         if (align ==='left' && this.fieldLabel.length) {
11880             
11881 //                Roo.log("left and has label");
11882                 cfg.cn = [
11883                     
11884                     {
11885                         tag: 'label',
11886                         'for' :  id,
11887                         cls : 'control-label col-sm-' + this.labelWidth,
11888                         html : this.fieldLabel
11889                         
11890                     },
11891                     {
11892                         cls : "col-sm-" + (12 - this.labelWidth), 
11893                         cn: [
11894                             combobox
11895                         ]
11896                     }
11897                     
11898                 ];
11899         } else if ( this.fieldLabel.length) {
11900 //                Roo.log(" label");
11901                  cfg.cn = [
11902                    
11903                     {
11904                         tag: 'label',
11905                         //cls : 'input-group-addon',
11906                         html : this.fieldLabel
11907                         
11908                     },
11909                     
11910                     combobox
11911                     
11912                 ];
11913
11914         } else {
11915             
11916 //                Roo.log(" no label && no align");
11917                 cfg = combobox
11918                      
11919                 
11920         }
11921          
11922         var settings=this;
11923         ['xs','sm','md','lg'].map(function(size){
11924             if (settings[size]) {
11925                 cfg.cls += ' col-' + size + '-' + settings[size];
11926             }
11927         });
11928         
11929         return cfg;
11930         
11931     },
11932     
11933     _initEventsCalled : false,
11934     
11935     // private
11936     initEvents: function()
11937     {
11938         
11939         if (this._initEventsCalled) { // as we call render... prevent looping...
11940             return;
11941         }
11942         this._initEventsCalled = true;
11943         
11944         if (!this.store) {
11945             throw "can not find store for combo";
11946         }
11947         
11948         this.store = Roo.factory(this.store, Roo.data);
11949         
11950         // if we are building from html. then this element is so complex, that we can not really
11951         // use the rendered HTML.
11952         // so we have to trash and replace the previous code.
11953         if (Roo.XComponent.build_from_html) {
11954             
11955             // remove this element....
11956             var e = this.el.dom, k=0;
11957             while (e ) { e = e.previousSibling;  ++k;}
11958
11959             this.el.remove();
11960             
11961             this.el=false;
11962             this.rendered = false;
11963             
11964             this.render(this.parent().getChildContainer(true), k);
11965             
11966             
11967             
11968         }
11969         
11970         
11971         /*
11972          * Touch Devices
11973          */
11974         
11975         if(Roo.isTouch && this.mobileTouchView){
11976             this.initTouchView();
11977             return;
11978         }
11979         
11980         if(this.tickable){
11981             this.initTickableEvents();
11982             return;
11983         }
11984         
11985         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11986         
11987         if(this.hiddenName){
11988             
11989             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11990             
11991             this.hiddenField.dom.value =
11992                 this.hiddenValue !== undefined ? this.hiddenValue :
11993                 this.value !== undefined ? this.value : '';
11994
11995             // prevent input submission
11996             this.el.dom.removeAttribute('name');
11997             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11998              
11999              
12000         }
12001         //if(Roo.isGecko){
12002         //    this.el.dom.setAttribute('autocomplete', 'off');
12003         //}
12004         
12005         var cls = 'x-combo-list';
12006         
12007         //this.list = new Roo.Layer({
12008         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12009         //});
12010         
12011         var _this = this;
12012         
12013         (function(){
12014             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12015             _this.list.setWidth(lw);
12016         }).defer(100);
12017         
12018         this.list.on('mouseover', this.onViewOver, this);
12019         this.list.on('mousemove', this.onViewMove, this);
12020         
12021         this.list.on('scroll', this.onViewScroll, this);
12022         
12023         /*
12024         this.list.swallowEvent('mousewheel');
12025         this.assetHeight = 0;
12026
12027         if(this.title){
12028             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12029             this.assetHeight += this.header.getHeight();
12030         }
12031
12032         this.innerList = this.list.createChild({cls:cls+'-inner'});
12033         this.innerList.on('mouseover', this.onViewOver, this);
12034         this.innerList.on('mousemove', this.onViewMove, this);
12035         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12036         
12037         if(this.allowBlank && !this.pageSize && !this.disableClear){
12038             this.footer = this.list.createChild({cls:cls+'-ft'});
12039             this.pageTb = new Roo.Toolbar(this.footer);
12040            
12041         }
12042         if(this.pageSize){
12043             this.footer = this.list.createChild({cls:cls+'-ft'});
12044             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12045                     {pageSize: this.pageSize});
12046             
12047         }
12048         
12049         if (this.pageTb && this.allowBlank && !this.disableClear) {
12050             var _this = this;
12051             this.pageTb.add(new Roo.Toolbar.Fill(), {
12052                 cls: 'x-btn-icon x-btn-clear',
12053                 text: '&#160;',
12054                 handler: function()
12055                 {
12056                     _this.collapse();
12057                     _this.clearValue();
12058                     _this.onSelect(false, -1);
12059                 }
12060             });
12061         }
12062         if (this.footer) {
12063             this.assetHeight += this.footer.getHeight();
12064         }
12065         */
12066             
12067         if(!this.tpl){
12068             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12069         }
12070
12071         this.view = new Roo.View(this.list, this.tpl, {
12072             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12073         });
12074         //this.view.wrapEl.setDisplayed(false);
12075         this.view.on('click', this.onViewClick, this);
12076         
12077         
12078         
12079         this.store.on('beforeload', this.onBeforeLoad, this);
12080         this.store.on('load', this.onLoad, this);
12081         this.store.on('loadexception', this.onLoadException, this);
12082         /*
12083         if(this.resizable){
12084             this.resizer = new Roo.Resizable(this.list,  {
12085                pinned:true, handles:'se'
12086             });
12087             this.resizer.on('resize', function(r, w, h){
12088                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12089                 this.listWidth = w;
12090                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12091                 this.restrictHeight();
12092             }, this);
12093             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12094         }
12095         */
12096         if(!this.editable){
12097             this.editable = true;
12098             this.setEditable(false);
12099         }
12100         
12101         /*
12102         
12103         if (typeof(this.events.add.listeners) != 'undefined') {
12104             
12105             this.addicon = this.wrap.createChild(
12106                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12107        
12108             this.addicon.on('click', function(e) {
12109                 this.fireEvent('add', this);
12110             }, this);
12111         }
12112         if (typeof(this.events.edit.listeners) != 'undefined') {
12113             
12114             this.editicon = this.wrap.createChild(
12115                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12116             if (this.addicon) {
12117                 this.editicon.setStyle('margin-left', '40px');
12118             }
12119             this.editicon.on('click', function(e) {
12120                 
12121                 // we fire even  if inothing is selected..
12122                 this.fireEvent('edit', this, this.lastData );
12123                 
12124             }, this);
12125         }
12126         */
12127         
12128         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12129             "up" : function(e){
12130                 this.inKeyMode = true;
12131                 this.selectPrev();
12132             },
12133
12134             "down" : function(e){
12135                 if(!this.isExpanded()){
12136                     this.onTriggerClick();
12137                 }else{
12138                     this.inKeyMode = true;
12139                     this.selectNext();
12140                 }
12141             },
12142
12143             "enter" : function(e){
12144 //                this.onViewClick();
12145                 //return true;
12146                 this.collapse();
12147                 
12148                 if(this.fireEvent("specialkey", this, e)){
12149                     this.onViewClick(false);
12150                 }
12151                 
12152                 return true;
12153             },
12154
12155             "esc" : function(e){
12156                 this.collapse();
12157             },
12158
12159             "tab" : function(e){
12160                 this.collapse();
12161                 
12162                 if(this.fireEvent("specialkey", this, e)){
12163                     this.onViewClick(false);
12164                 }
12165                 
12166                 return true;
12167             },
12168
12169             scope : this,
12170
12171             doRelay : function(foo, bar, hname){
12172                 if(hname == 'down' || this.scope.isExpanded()){
12173                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12174                 }
12175                 return true;
12176             },
12177
12178             forceKeyDown: true
12179         });
12180         
12181         
12182         this.queryDelay = Math.max(this.queryDelay || 10,
12183                 this.mode == 'local' ? 10 : 250);
12184         
12185         
12186         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12187         
12188         if(this.typeAhead){
12189             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12190         }
12191         if(this.editable !== false){
12192             this.inputEl().on("keyup", this.onKeyUp, this);
12193         }
12194         if(this.forceSelection){
12195             this.inputEl().on('blur', this.doForce, this);
12196         }
12197         
12198         if(this.multiple){
12199             this.choices = this.el.select('ul.select2-choices', true).first();
12200             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12201         }
12202     },
12203     
12204     initTickableEvents: function()
12205     {   
12206         this.createList();
12207         
12208         if(this.hiddenName){
12209             
12210             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12211             
12212             this.hiddenField.dom.value =
12213                 this.hiddenValue !== undefined ? this.hiddenValue :
12214                 this.value !== undefined ? this.value : '';
12215
12216             // prevent input submission
12217             this.el.dom.removeAttribute('name');
12218             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12219              
12220              
12221         }
12222         
12223 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12224         
12225         this.choices = this.el.select('ul.select2-choices', true).first();
12226         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12227         if(this.triggerList){
12228             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12229         }
12230          
12231         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12232         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12233         
12234         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12235         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12236         
12237         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12238         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12239         
12240         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12241         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12242         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12243         
12244         this.okBtn.hide();
12245         this.cancelBtn.hide();
12246         
12247         var _this = this;
12248         
12249         (function(){
12250             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12251             _this.list.setWidth(lw);
12252         }).defer(100);
12253         
12254         this.list.on('mouseover', this.onViewOver, this);
12255         this.list.on('mousemove', this.onViewMove, this);
12256         
12257         this.list.on('scroll', this.onViewScroll, this);
12258         
12259         if(!this.tpl){
12260             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>';
12261         }
12262
12263         this.view = new Roo.View(this.list, this.tpl, {
12264             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12265         });
12266         
12267         //this.view.wrapEl.setDisplayed(false);
12268         this.view.on('click', this.onViewClick, this);
12269         
12270         
12271         
12272         this.store.on('beforeload', this.onBeforeLoad, this);
12273         this.store.on('load', this.onLoad, this);
12274         this.store.on('loadexception', this.onLoadException, this);
12275         
12276         if(this.editable){
12277             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12278                 "up" : function(e){
12279                     this.inKeyMode = true;
12280                     this.selectPrev();
12281                 },
12282
12283                 "down" : function(e){
12284                     this.inKeyMode = true;
12285                     this.selectNext();
12286                 },
12287
12288                 "enter" : function(e){
12289                     if(this.fireEvent("specialkey", this, e)){
12290                         this.onViewClick(false);
12291                     }
12292                     
12293                     return true;
12294                 },
12295
12296                 "esc" : function(e){
12297                     this.onTickableFooterButtonClick(e, false, false);
12298                 },
12299
12300                 "tab" : function(e){
12301                     this.fireEvent("specialkey", this, e);
12302                     
12303                     this.onTickableFooterButtonClick(e, false, false);
12304                     
12305                     return true;
12306                 },
12307
12308                 scope : this,
12309
12310                 doRelay : function(e, fn, key){
12311                     if(this.scope.isExpanded()){
12312                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12313                     }
12314                     return true;
12315                 },
12316
12317                 forceKeyDown: true
12318             });
12319         }
12320         
12321         this.queryDelay = Math.max(this.queryDelay || 10,
12322                 this.mode == 'local' ? 10 : 250);
12323         
12324         
12325         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12326         
12327         if(this.typeAhead){
12328             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12329         }
12330         
12331         if(this.editable !== false){
12332             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12333         }
12334         
12335     },
12336
12337     onDestroy : function(){
12338         if(this.view){
12339             this.view.setStore(null);
12340             this.view.el.removeAllListeners();
12341             this.view.el.remove();
12342             this.view.purgeListeners();
12343         }
12344         if(this.list){
12345             this.list.dom.innerHTML  = '';
12346         }
12347         
12348         if(this.store){
12349             this.store.un('beforeload', this.onBeforeLoad, this);
12350             this.store.un('load', this.onLoad, this);
12351             this.store.un('loadexception', this.onLoadException, this);
12352         }
12353         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12354     },
12355
12356     // private
12357     fireKey : function(e){
12358         if(e.isNavKeyPress() && !this.list.isVisible()){
12359             this.fireEvent("specialkey", this, e);
12360         }
12361     },
12362
12363     // private
12364     onResize: function(w, h){
12365 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12366 //        
12367 //        if(typeof w != 'number'){
12368 //            // we do not handle it!?!?
12369 //            return;
12370 //        }
12371 //        var tw = this.trigger.getWidth();
12372 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12373 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12374 //        var x = w - tw;
12375 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12376 //            
12377 //        //this.trigger.setStyle('left', x+'px');
12378 //        
12379 //        if(this.list && this.listWidth === undefined){
12380 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12381 //            this.list.setWidth(lw);
12382 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12383 //        }
12384         
12385     
12386         
12387     },
12388
12389     /**
12390      * Allow or prevent the user from directly editing the field text.  If false is passed,
12391      * the user will only be able to select from the items defined in the dropdown list.  This method
12392      * is the runtime equivalent of setting the 'editable' config option at config time.
12393      * @param {Boolean} value True to allow the user to directly edit the field text
12394      */
12395     setEditable : function(value){
12396         if(value == this.editable){
12397             return;
12398         }
12399         this.editable = value;
12400         if(!value){
12401             this.inputEl().dom.setAttribute('readOnly', true);
12402             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12403             this.inputEl().addClass('x-combo-noedit');
12404         }else{
12405             this.inputEl().dom.setAttribute('readOnly', false);
12406             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12407             this.inputEl().removeClass('x-combo-noedit');
12408         }
12409     },
12410
12411     // private
12412     
12413     onBeforeLoad : function(combo,opts){
12414         if(!this.hasFocus){
12415             return;
12416         }
12417          if (!opts.add) {
12418             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12419          }
12420         this.restrictHeight();
12421         this.selectedIndex = -1;
12422     },
12423
12424     // private
12425     onLoad : function(){
12426         
12427         this.hasQuery = false;
12428         
12429         if(!this.hasFocus){
12430             return;
12431         }
12432         
12433         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12434             this.loading.hide();
12435         }
12436              
12437         if(this.store.getCount() > 0){
12438             this.expand();
12439             this.restrictHeight();
12440             if(this.lastQuery == this.allQuery){
12441                 if(this.editable && !this.tickable){
12442                     this.inputEl().dom.select();
12443                 }
12444                 
12445                 if(
12446                     !this.selectByValue(this.value, true) &&
12447                     this.autoFocus && 
12448                     (
12449                         !this.store.lastOptions ||
12450                         typeof(this.store.lastOptions.add) == 'undefined' || 
12451                         this.store.lastOptions.add != true
12452                     )
12453                 ){
12454                     this.select(0, true);
12455                 }
12456             }else{
12457                 if(this.autoFocus){
12458                     this.selectNext();
12459                 }
12460                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12461                     this.taTask.delay(this.typeAheadDelay);
12462                 }
12463             }
12464         }else{
12465             this.onEmptyResults();
12466         }
12467         
12468         //this.el.focus();
12469     },
12470     // private
12471     onLoadException : function()
12472     {
12473         this.hasQuery = false;
12474         
12475         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12476             this.loading.hide();
12477         }
12478         
12479         if(this.tickable && this.editable){
12480             return;
12481         }
12482         
12483         this.collapse();
12484         // only causes errors at present
12485         //Roo.log(this.store.reader.jsonData);
12486         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12487             // fixme
12488             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12489         //}
12490         
12491         
12492     },
12493     // private
12494     onTypeAhead : function(){
12495         if(this.store.getCount() > 0){
12496             var r = this.store.getAt(0);
12497             var newValue = r.data[this.displayField];
12498             var len = newValue.length;
12499             var selStart = this.getRawValue().length;
12500             
12501             if(selStart != len){
12502                 this.setRawValue(newValue);
12503                 this.selectText(selStart, newValue.length);
12504             }
12505         }
12506     },
12507
12508     // private
12509     onSelect : function(record, index){
12510         
12511         if(this.fireEvent('beforeselect', this, record, index) !== false){
12512         
12513             this.setFromData(index > -1 ? record.data : false);
12514             
12515             this.collapse();
12516             this.fireEvent('select', this, record, index);
12517         }
12518     },
12519
12520     /**
12521      * Returns the currently selected field value or empty string if no value is set.
12522      * @return {String} value The selected value
12523      */
12524     getValue : function(){
12525         
12526         if(this.multiple){
12527             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12528         }
12529         
12530         if(this.valueField){
12531             return typeof this.value != 'undefined' ? this.value : '';
12532         }else{
12533             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12534         }
12535     },
12536
12537     /**
12538      * Clears any text/value currently set in the field
12539      */
12540     clearValue : function(){
12541         if(this.hiddenField){
12542             this.hiddenField.dom.value = '';
12543         }
12544         this.value = '';
12545         this.setRawValue('');
12546         this.lastSelectionText = '';
12547         this.lastData = false;
12548         
12549         var close = this.closeTriggerEl();
12550         
12551         if(close){
12552             close.hide();
12553         }
12554         
12555     },
12556
12557     /**
12558      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12559      * will be displayed in the field.  If the value does not match the data value of an existing item,
12560      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12561      * Otherwise the field will be blank (although the value will still be set).
12562      * @param {String} value The value to match
12563      */
12564     setValue : function(v){
12565         if(this.multiple){
12566             this.syncValue();
12567             return;
12568         }
12569         
12570         var text = v;
12571         if(this.valueField){
12572             var r = this.findRecord(this.valueField, v);
12573             if(r){
12574                 text = r.data[this.displayField];
12575             }else if(this.valueNotFoundText !== undefined){
12576                 text = this.valueNotFoundText;
12577             }
12578         }
12579         this.lastSelectionText = text;
12580         if(this.hiddenField){
12581             this.hiddenField.dom.value = v;
12582         }
12583         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12584         this.value = v;
12585         
12586         var close = this.closeTriggerEl();
12587         
12588         if(close){
12589             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12590         }
12591     },
12592     /**
12593      * @property {Object} the last set data for the element
12594      */
12595     
12596     lastData : false,
12597     /**
12598      * Sets the value of the field based on a object which is related to the record format for the store.
12599      * @param {Object} value the value to set as. or false on reset?
12600      */
12601     setFromData : function(o){
12602         
12603         if(this.multiple){
12604             this.addItem(o);
12605             return;
12606         }
12607             
12608         var dv = ''; // display value
12609         var vv = ''; // value value..
12610         this.lastData = o;
12611         if (this.displayField) {
12612             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12613         } else {
12614             // this is an error condition!!!
12615             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12616         }
12617         
12618         if(this.valueField){
12619             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12620         }
12621         
12622         var close = this.closeTriggerEl();
12623         
12624         if(close){
12625             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12626         }
12627         
12628         if(this.hiddenField){
12629             this.hiddenField.dom.value = vv;
12630             
12631             this.lastSelectionText = dv;
12632             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12633             this.value = vv;
12634             return;
12635         }
12636         // no hidden field.. - we store the value in 'value', but still display
12637         // display field!!!!
12638         this.lastSelectionText = dv;
12639         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12640         this.value = vv;
12641         
12642         
12643         
12644     },
12645     // private
12646     reset : function(){
12647         // overridden so that last data is reset..
12648         
12649         if(this.multiple){
12650             this.clearItem();
12651             return;
12652         }
12653         
12654         this.setValue(this.originalValue);
12655         this.clearInvalid();
12656         this.lastData = false;
12657         if (this.view) {
12658             this.view.clearSelections();
12659         }
12660     },
12661     // private
12662     findRecord : function(prop, value){
12663         var record;
12664         if(this.store.getCount() > 0){
12665             this.store.each(function(r){
12666                 if(r.data[prop] == value){
12667                     record = r;
12668                     return false;
12669                 }
12670                 return true;
12671             });
12672         }
12673         return record;
12674     },
12675     
12676     getName: function()
12677     {
12678         // returns hidden if it's set..
12679         if (!this.rendered) {return ''};
12680         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12681         
12682     },
12683     // private
12684     onViewMove : function(e, t){
12685         this.inKeyMode = false;
12686     },
12687
12688     // private
12689     onViewOver : function(e, t){
12690         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12691             return;
12692         }
12693         var item = this.view.findItemFromChild(t);
12694         
12695         if(item){
12696             var index = this.view.indexOf(item);
12697             this.select(index, false);
12698         }
12699     },
12700
12701     // private
12702     onViewClick : function(view, doFocus, el, e)
12703     {
12704         var index = this.view.getSelectedIndexes()[0];
12705         
12706         var r = this.store.getAt(index);
12707         
12708         if(this.tickable){
12709             
12710             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12711                 return;
12712             }
12713             
12714             var rm = false;
12715             var _this = this;
12716             
12717             Roo.each(this.tickItems, function(v,k){
12718                 
12719                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12720                     Roo.log(v);
12721                     _this.tickItems.splice(k, 1);
12722                     
12723                     if(typeof(e) == 'undefined' && view == false){
12724                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12725                     }
12726                     
12727                     rm = true;
12728                     return;
12729                 }
12730             });
12731             
12732             if(rm){
12733                 return;
12734             }
12735             
12736             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12737                 this.tickItems.push(r.data);
12738             }
12739             
12740             if(typeof(e) == 'undefined' && view == false){
12741                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12742             }
12743                     
12744             return;
12745         }
12746         
12747         if(r){
12748             this.onSelect(r, index);
12749         }
12750         if(doFocus !== false && !this.blockFocus){
12751             this.inputEl().focus();
12752         }
12753     },
12754
12755     // private
12756     restrictHeight : function(){
12757         //this.innerList.dom.style.height = '';
12758         //var inner = this.innerList.dom;
12759         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12760         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12761         //this.list.beginUpdate();
12762         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12763         this.list.alignTo(this.inputEl(), this.listAlign);
12764         this.list.alignTo(this.inputEl(), this.listAlign);
12765         //this.list.endUpdate();
12766     },
12767
12768     // private
12769     onEmptyResults : function(){
12770         
12771         if(this.tickable && this.editable){
12772             this.restrictHeight();
12773             return;
12774         }
12775         
12776         this.collapse();
12777     },
12778
12779     /**
12780      * Returns true if the dropdown list is expanded, else false.
12781      */
12782     isExpanded : function(){
12783         return this.list.isVisible();
12784     },
12785
12786     /**
12787      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12788      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12789      * @param {String} value The data value of the item to select
12790      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12791      * selected item if it is not currently in view (defaults to true)
12792      * @return {Boolean} True if the value matched an item in the list, else false
12793      */
12794     selectByValue : function(v, scrollIntoView){
12795         if(v !== undefined && v !== null){
12796             var r = this.findRecord(this.valueField || this.displayField, v);
12797             if(r){
12798                 this.select(this.store.indexOf(r), scrollIntoView);
12799                 return true;
12800             }
12801         }
12802         return false;
12803     },
12804
12805     /**
12806      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12807      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12808      * @param {Number} index The zero-based index of the list item to select
12809      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12810      * selected item if it is not currently in view (defaults to true)
12811      */
12812     select : function(index, scrollIntoView){
12813         this.selectedIndex = index;
12814         this.view.select(index);
12815         if(scrollIntoView !== false){
12816             var el = this.view.getNode(index);
12817             /*
12818              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12819              */
12820             if(el){
12821                 this.list.scrollChildIntoView(el, false);
12822             }
12823         }
12824     },
12825
12826     // private
12827     selectNext : function(){
12828         var ct = this.store.getCount();
12829         if(ct > 0){
12830             if(this.selectedIndex == -1){
12831                 this.select(0);
12832             }else if(this.selectedIndex < ct-1){
12833                 this.select(this.selectedIndex+1);
12834             }
12835         }
12836     },
12837
12838     // private
12839     selectPrev : function(){
12840         var ct = this.store.getCount();
12841         if(ct > 0){
12842             if(this.selectedIndex == -1){
12843                 this.select(0);
12844             }else if(this.selectedIndex != 0){
12845                 this.select(this.selectedIndex-1);
12846             }
12847         }
12848     },
12849
12850     // private
12851     onKeyUp : function(e){
12852         if(this.editable !== false && !e.isSpecialKey()){
12853             this.lastKey = e.getKey();
12854             this.dqTask.delay(this.queryDelay);
12855         }
12856     },
12857
12858     // private
12859     validateBlur : function(){
12860         return !this.list || !this.list.isVisible();   
12861     },
12862
12863     // private
12864     initQuery : function(){
12865         
12866         var v = this.getRawValue();
12867         
12868         if(this.tickable && this.editable){
12869             v = this.tickableInputEl().getValue();
12870         }
12871         
12872         this.doQuery(v);
12873     },
12874
12875     // private
12876     doForce : function(){
12877         if(this.inputEl().dom.value.length > 0){
12878             this.inputEl().dom.value =
12879                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12880              
12881         }
12882     },
12883
12884     /**
12885      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12886      * query allowing the query action to be canceled if needed.
12887      * @param {String} query The SQL query to execute
12888      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12889      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12890      * saved in the current store (defaults to false)
12891      */
12892     doQuery : function(q, forceAll){
12893         
12894         if(q === undefined || q === null){
12895             q = '';
12896         }
12897         var qe = {
12898             query: q,
12899             forceAll: forceAll,
12900             combo: this,
12901             cancel:false
12902         };
12903         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12904             return false;
12905         }
12906         q = qe.query;
12907         
12908         forceAll = qe.forceAll;
12909         if(forceAll === true || (q.length >= this.minChars)){
12910             
12911             this.hasQuery = true;
12912             
12913             if(this.lastQuery != q || this.alwaysQuery){
12914                 this.lastQuery = q;
12915                 if(this.mode == 'local'){
12916                     this.selectedIndex = -1;
12917                     if(forceAll){
12918                         this.store.clearFilter();
12919                     }else{
12920                         
12921                         if(this.specialFilter){
12922                             this.fireEvent('specialfilter', this);
12923                             this.onLoad();
12924                             return;
12925                         }
12926                         
12927                         this.store.filter(this.displayField, q);
12928                     }
12929                     
12930                     this.store.fireEvent("datachanged", this.store);
12931                     
12932                     this.onLoad();
12933                     
12934                     
12935                 }else{
12936                     
12937                     this.store.baseParams[this.queryParam] = q;
12938                     
12939                     var options = {params : this.getParams(q)};
12940                     
12941                     if(this.loadNext){
12942                         options.add = true;
12943                         options.params.start = this.page * this.pageSize;
12944                     }
12945                     
12946                     this.store.load(options);
12947                     
12948                     /*
12949                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12950                      *  we should expand the list on onLoad
12951                      *  so command out it
12952                      */
12953 //                    this.expand();
12954                 }
12955             }else{
12956                 this.selectedIndex = -1;
12957                 this.onLoad();   
12958             }
12959         }
12960         
12961         this.loadNext = false;
12962     },
12963     
12964     // private
12965     getParams : function(q){
12966         var p = {};
12967         //p[this.queryParam] = q;
12968         
12969         if(this.pageSize){
12970             p.start = 0;
12971             p.limit = this.pageSize;
12972         }
12973         return p;
12974     },
12975
12976     /**
12977      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12978      */
12979     collapse : function(){
12980         if(!this.isExpanded()){
12981             return;
12982         }
12983         
12984         this.list.hide();
12985         
12986         if(this.tickable){
12987             this.hasFocus = false;
12988             this.okBtn.hide();
12989             this.cancelBtn.hide();
12990             this.trigger.show();
12991             
12992             if(this.editable){
12993                 this.tickableInputEl().dom.value = '';
12994                 this.tickableInputEl().blur();
12995             }
12996             
12997         }
12998         
12999         Roo.get(document).un('mousedown', this.collapseIf, this);
13000         Roo.get(document).un('mousewheel', this.collapseIf, this);
13001         if (!this.editable) {
13002             Roo.get(document).un('keydown', this.listKeyPress, this);
13003         }
13004         this.fireEvent('collapse', this);
13005     },
13006
13007     // private
13008     collapseIf : function(e){
13009         var in_combo  = e.within(this.el);
13010         var in_list =  e.within(this.list);
13011         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13012         
13013         if (in_combo || in_list || is_list) {
13014             //e.stopPropagation();
13015             return;
13016         }
13017         
13018         if(this.tickable){
13019             this.onTickableFooterButtonClick(e, false, false);
13020         }
13021
13022         this.collapse();
13023         
13024     },
13025
13026     /**
13027      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13028      */
13029     expand : function(){
13030        
13031         if(this.isExpanded() || !this.hasFocus){
13032             return;
13033         }
13034         
13035         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13036         this.list.setWidth(lw);
13037         
13038         
13039          Roo.log('expand');
13040         
13041         this.list.show();
13042         
13043         this.restrictHeight();
13044         
13045         if(this.tickable){
13046             
13047             this.tickItems = Roo.apply([], this.item);
13048             
13049             this.okBtn.show();
13050             this.cancelBtn.show();
13051             this.trigger.hide();
13052             
13053             if(this.editable){
13054                 this.tickableInputEl().focus();
13055             }
13056             
13057         }
13058         
13059         Roo.get(document).on('mousedown', this.collapseIf, this);
13060         Roo.get(document).on('mousewheel', this.collapseIf, this);
13061         if (!this.editable) {
13062             Roo.get(document).on('keydown', this.listKeyPress, this);
13063         }
13064         
13065         this.fireEvent('expand', this);
13066     },
13067
13068     // private
13069     // Implements the default empty TriggerField.onTriggerClick function
13070     onTriggerClick : function(e)
13071     {
13072         Roo.log('trigger click');
13073         
13074         if(this.disabled || !this.triggerList){
13075             return;
13076         }
13077         
13078         this.page = 0;
13079         this.loadNext = false;
13080         
13081         if(this.isExpanded()){
13082             this.collapse();
13083             if (!this.blockFocus) {
13084                 this.inputEl().focus();
13085             }
13086             
13087         }else {
13088             this.hasFocus = true;
13089             if(this.triggerAction == 'all') {
13090                 this.doQuery(this.allQuery, true);
13091             } else {
13092                 this.doQuery(this.getRawValue());
13093             }
13094             if (!this.blockFocus) {
13095                 this.inputEl().focus();
13096             }
13097         }
13098     },
13099     
13100     onTickableTriggerClick : function(e)
13101     {
13102         if(this.disabled){
13103             return;
13104         }
13105         
13106         this.page = 0;
13107         this.loadNext = false;
13108         this.hasFocus = true;
13109         
13110         if(this.triggerAction == 'all') {
13111             this.doQuery(this.allQuery, true);
13112         } else {
13113             this.doQuery(this.getRawValue());
13114         }
13115     },
13116     
13117     onSearchFieldClick : function(e)
13118     {
13119         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13120             this.onTickableFooterButtonClick(e, false, false);
13121             return;
13122         }
13123         
13124         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13125             return;
13126         }
13127         
13128         this.page = 0;
13129         this.loadNext = false;
13130         this.hasFocus = true;
13131         
13132         if(this.triggerAction == 'all') {
13133             this.doQuery(this.allQuery, true);
13134         } else {
13135             this.doQuery(this.getRawValue());
13136         }
13137     },
13138     
13139     listKeyPress : function(e)
13140     {
13141         //Roo.log('listkeypress');
13142         // scroll to first matching element based on key pres..
13143         if (e.isSpecialKey()) {
13144             return false;
13145         }
13146         var k = String.fromCharCode(e.getKey()).toUpperCase();
13147         //Roo.log(k);
13148         var match  = false;
13149         var csel = this.view.getSelectedNodes();
13150         var cselitem = false;
13151         if (csel.length) {
13152             var ix = this.view.indexOf(csel[0]);
13153             cselitem  = this.store.getAt(ix);
13154             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13155                 cselitem = false;
13156             }
13157             
13158         }
13159         
13160         this.store.each(function(v) { 
13161             if (cselitem) {
13162                 // start at existing selection.
13163                 if (cselitem.id == v.id) {
13164                     cselitem = false;
13165                 }
13166                 return true;
13167             }
13168                 
13169             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13170                 match = this.store.indexOf(v);
13171                 return false;
13172             }
13173             return true;
13174         }, this);
13175         
13176         if (match === false) {
13177             return true; // no more action?
13178         }
13179         // scroll to?
13180         this.view.select(match);
13181         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13182         sn.scrollIntoView(sn.dom.parentNode, false);
13183     },
13184     
13185     onViewScroll : function(e, t){
13186         
13187         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){
13188             return;
13189         }
13190         
13191         this.hasQuery = true;
13192         
13193         this.loading = this.list.select('.loading', true).first();
13194         
13195         if(this.loading === null){
13196             this.list.createChild({
13197                 tag: 'div',
13198                 cls: 'loading select2-more-results select2-active',
13199                 html: 'Loading more results...'
13200             });
13201             
13202             this.loading = this.list.select('.loading', true).first();
13203             
13204             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13205             
13206             this.loading.hide();
13207         }
13208         
13209         this.loading.show();
13210         
13211         var _combo = this;
13212         
13213         this.page++;
13214         this.loadNext = true;
13215         
13216         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13217         
13218         return;
13219     },
13220     
13221     addItem : function(o)
13222     {   
13223         var dv = ''; // display value
13224         
13225         if (this.displayField) {
13226             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13227         } else {
13228             // this is an error condition!!!
13229             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13230         }
13231         
13232         if(!dv.length){
13233             return;
13234         }
13235         
13236         var choice = this.choices.createChild({
13237             tag: 'li',
13238             cls: 'select2-search-choice',
13239             cn: [
13240                 {
13241                     tag: 'div',
13242                     html: dv
13243                 },
13244                 {
13245                     tag: 'a',
13246                     href: '#',
13247                     cls: 'select2-search-choice-close',
13248                     tabindex: '-1'
13249                 }
13250             ]
13251             
13252         }, this.searchField);
13253         
13254         var close = choice.select('a.select2-search-choice-close', true).first();
13255         
13256         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13257         
13258         this.item.push(o);
13259         
13260         this.lastData = o;
13261         
13262         this.syncValue();
13263         
13264         this.inputEl().dom.value = '';
13265         
13266         this.validate();
13267     },
13268     
13269     onRemoveItem : function(e, _self, o)
13270     {
13271         e.preventDefault();
13272         
13273         this.lastItem = Roo.apply([], this.item);
13274         
13275         var index = this.item.indexOf(o.data) * 1;
13276         
13277         if( index < 0){
13278             Roo.log('not this item?!');
13279             return;
13280         }
13281         
13282         this.item.splice(index, 1);
13283         o.item.remove();
13284         
13285         this.syncValue();
13286         
13287         this.fireEvent('remove', this, e);
13288         
13289         this.validate();
13290         
13291     },
13292     
13293     syncValue : function()
13294     {
13295         if(!this.item.length){
13296             this.clearValue();
13297             return;
13298         }
13299             
13300         var value = [];
13301         var _this = this;
13302         Roo.each(this.item, function(i){
13303             if(_this.valueField){
13304                 value.push(i[_this.valueField]);
13305                 return;
13306             }
13307
13308             value.push(i);
13309         });
13310
13311         this.value = value.join(',');
13312
13313         if(this.hiddenField){
13314             this.hiddenField.dom.value = this.value;
13315         }
13316         
13317         this.store.fireEvent("datachanged", this.store);
13318     },
13319     
13320     clearItem : function()
13321     {
13322         if(!this.multiple){
13323             return;
13324         }
13325         
13326         this.item = [];
13327         
13328         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13329            c.remove();
13330         });
13331         
13332         this.syncValue();
13333         
13334         this.validate();
13335         
13336         if(this.tickable && !Roo.isTouch){
13337             this.view.refresh();
13338         }
13339     },
13340     
13341     inputEl: function ()
13342     {
13343         if(Roo.isTouch && this.mobileTouchView){
13344             return this.el.select('input.form-control',true).first();
13345         }
13346         
13347         if(this.tickable){
13348             return this.searchField;
13349         }
13350         
13351         return this.el.select('input.form-control',true).first();
13352     },
13353     
13354     
13355     onTickableFooterButtonClick : function(e, btn, el)
13356     {
13357         e.preventDefault();
13358         
13359         this.lastItem = Roo.apply([], this.item);
13360         
13361         if(btn && btn.name == 'cancel'){
13362             this.tickItems = Roo.apply([], this.item);
13363             this.collapse();
13364             return;
13365         }
13366         
13367         this.clearItem();
13368         
13369         var _this = this;
13370         
13371         Roo.each(this.tickItems, function(o){
13372             _this.addItem(o);
13373         });
13374         
13375         this.collapse();
13376         
13377     },
13378     
13379     validate : function()
13380     {
13381         var v = this.getRawValue();
13382         
13383         if(this.multiple){
13384             v = this.getValue();
13385         }
13386         
13387         if(this.disabled || this.allowBlank || v.length){
13388             this.markValid();
13389             return true;
13390         }
13391         
13392         this.markInvalid();
13393         return false;
13394     },
13395     
13396     tickableInputEl : function()
13397     {
13398         if(!this.tickable || !this.editable){
13399             return this.inputEl();
13400         }
13401         
13402         return this.inputEl().select('.select2-search-field-input', true).first();
13403     },
13404     
13405     
13406     getAutoCreateTouchView : function()
13407     {
13408         var id = Roo.id();
13409         
13410         var cfg = {
13411             cls: 'form-group' //input-group
13412         };
13413         
13414         var input =  {
13415             tag: 'input',
13416             id : id,
13417             type : this.inputType,
13418             cls : 'form-control x-combo-noedit',
13419             autocomplete: 'new-password',
13420             placeholder : this.placeholder || '',
13421             readonly : true
13422         };
13423         
13424         if (this.name) {
13425             input.name = this.name;
13426         }
13427         
13428         if (this.size) {
13429             input.cls += ' input-' + this.size;
13430         }
13431         
13432         if (this.disabled) {
13433             input.disabled = true;
13434         }
13435         
13436         var inputblock = {
13437             cls : '',
13438             cn : [
13439                 input
13440             ]
13441         };
13442         
13443         if(this.before){
13444             inputblock.cls += ' input-group';
13445             
13446             inputblock.cn.unshift({
13447                 tag :'span',
13448                 cls : 'input-group-addon',
13449                 html : this.before
13450             });
13451         }
13452         
13453         if(this.removable && !this.multiple){
13454             inputblock.cls += ' roo-removable';
13455             
13456             inputblock.cn.push({
13457                 tag: 'button',
13458                 html : 'x',
13459                 cls : 'roo-combo-removable-btn close'
13460             });
13461         }
13462
13463         if(this.hasFeedback && !this.allowBlank){
13464             
13465             inputblock.cls += ' has-feedback';
13466             
13467             inputblock.cn.push({
13468                 tag: 'span',
13469                 cls: 'glyphicon form-control-feedback'
13470             });
13471             
13472         }
13473         
13474         if (this.after) {
13475             
13476             inputblock.cls += (this.before) ? '' : ' input-group';
13477             
13478             inputblock.cn.push({
13479                 tag :'span',
13480                 cls : 'input-group-addon',
13481                 html : this.after
13482             });
13483         }
13484
13485         var box = {
13486             tag: 'div',
13487             cn: [
13488                 {
13489                     tag: 'input',
13490                     type : 'hidden',
13491                     cls: 'form-hidden-field'
13492                 },
13493                 inputblock
13494             ]
13495             
13496         };
13497         
13498         if(this.multiple){
13499             box = {
13500                 tag: 'div',
13501                 cn: [
13502                     {
13503                         tag: 'input',
13504                         type : 'hidden',
13505                         cls: 'form-hidden-field'
13506                     },
13507                     {
13508                         tag: 'ul',
13509                         cls: 'select2-choices',
13510                         cn:[
13511                             {
13512                                 tag: 'li',
13513                                 cls: 'select2-search-field',
13514                                 cn: [
13515
13516                                     inputblock
13517                                 ]
13518                             }
13519                         ]
13520                     }
13521                 ]
13522             }
13523         };
13524         
13525         var combobox = {
13526             cls: 'select2-container input-group',
13527             cn: [
13528                 box
13529             ]
13530         };
13531         
13532         if(this.multiple){
13533             combobox.cls += ' select2-container-multi';
13534         }
13535         
13536         var align = this.labelAlign || this.parentLabelAlign();
13537         
13538         cfg.cn = combobox;
13539         
13540         if(this.fieldLabel.length){
13541             
13542             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13543             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13544             
13545             cfg.cn = [
13546                 {
13547                     tag: 'label',
13548                     cls : 'control-label ' + lw,
13549                     html : this.fieldLabel
13550
13551                 },
13552                 {
13553                     cls : cw, 
13554                     cn: [
13555                         combobox
13556                     ]
13557                 }
13558             ];
13559         }
13560         
13561         var settings = this;
13562         
13563         ['xs','sm','md','lg'].map(function(size){
13564             if (settings[size]) {
13565                 cfg.cls += ' col-' + size + '-' + settings[size];
13566             }
13567         });
13568         
13569         return cfg;
13570     },
13571     
13572     initTouchView : function()
13573     {
13574         this.renderTouchView();
13575         
13576         this.touchViewEl.on('scroll', function(){
13577             this.el.dom.scrollTop = 0;
13578         }, this);
13579         
13580         this.originalValue = this.getValue();
13581         
13582         this.inputEl().on("click", this.showTouchView, this);
13583         
13584         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13585         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13586         
13587         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13588         
13589         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13590         this.store.on('load', this.onTouchViewLoad, this);
13591         this.store.on('loadexception', this.onTouchViewLoadException, this);
13592         
13593         if(this.hiddenName){
13594             
13595             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13596             
13597             this.hiddenField.dom.value =
13598                 this.hiddenValue !== undefined ? this.hiddenValue :
13599                 this.value !== undefined ? this.value : '';
13600         
13601             this.el.dom.removeAttribute('name');
13602             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13603         }
13604         
13605         if(this.multiple){
13606             this.choices = this.el.select('ul.select2-choices', true).first();
13607             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13608         }
13609         
13610         if(this.removable && !this.multiple){
13611             var close = this.closeTriggerEl();
13612             if(close){
13613                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13614                 close.on('click', this.removeBtnClick, this, close);
13615             }
13616         }
13617         /*
13618          * fix the bug in Safari iOS8
13619          */
13620         this.inputEl().on("focus", function(e){
13621             document.activeElement.blur();
13622         }, this);
13623         
13624         return;
13625         
13626         
13627     },
13628     
13629     renderTouchView : function()
13630     {
13631         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13632         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13633         
13634         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13635         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13636         
13637         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13638         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13639         this.touchViewBodyEl.setStyle('overflow', 'auto');
13640         
13641         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13642         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13643         
13644         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13645         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13646         
13647     },
13648     
13649     showTouchView : function()
13650     {
13651         if(this.disabled){
13652             return;
13653         }
13654         
13655         this.touchViewHeaderEl.hide();
13656
13657         if(this.fieldLabel.length){
13658             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13659             this.touchViewHeaderEl.show();
13660         }
13661
13662         this.touchViewEl.show();
13663
13664         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13665         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13666
13667         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13668
13669         if(this.fieldLabel.length){
13670             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13671         }
13672         
13673         this.touchViewBodyEl.setHeight(bodyHeight);
13674
13675         if(this.animate){
13676             var _this = this;
13677             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13678         }else{
13679             this.touchViewEl.addClass('in');
13680         }
13681
13682         this.doTouchViewQuery();
13683         
13684     },
13685     
13686     hideTouchView : function()
13687     {
13688         this.touchViewEl.removeClass('in');
13689
13690         if(this.animate){
13691             var _this = this;
13692             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13693         }else{
13694             this.touchViewEl.setStyle('display', 'none');
13695         }
13696         
13697     },
13698     
13699     setTouchViewValue : function()
13700     {
13701         if(this.multiple){
13702             this.clearItem();
13703         
13704             var _this = this;
13705
13706             Roo.each(this.tickItems, function(o){
13707                 this.addItem(o);
13708             }, this);
13709         }
13710         
13711         this.hideTouchView();
13712     },
13713     
13714     doTouchViewQuery : function()
13715     {
13716         var qe = {
13717             query: '',
13718             forceAll: true,
13719             combo: this,
13720             cancel:false
13721         };
13722         
13723         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13724             return false;
13725         }
13726         
13727         if(!this.alwaysQuery || this.mode == 'local'){
13728             this.onTouchViewLoad();
13729             return;
13730         }
13731         
13732         this.store.load();
13733     },
13734     
13735     onTouchViewBeforeLoad : function(combo,opts)
13736     {
13737         return;
13738     },
13739
13740     // private
13741     onTouchViewLoad : function()
13742     {
13743         if(this.store.getCount() < 1){
13744             this.onTouchViewEmptyResults();
13745             return;
13746         }
13747         
13748         this.clearTouchView();
13749         
13750         var rawValue = this.getRawValue();
13751         
13752         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13753         
13754         this.tickItems = [];
13755         
13756         this.store.data.each(function(d, rowIndex){
13757             var row = this.touchViewListGroup.createChild(template);
13758             
13759             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13760                 var cfg = {
13761                     data : d.data,
13762                     html : d.data[this.displayField]
13763                 };
13764                 
13765                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13766                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13767                 }
13768             }
13769             
13770             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13771                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13772             }
13773             
13774             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13775                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13776                 this.tickItems.push(d.data);
13777             }
13778             
13779             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13780             
13781         }, this);
13782         
13783         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13784         
13785         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13786
13787         if(this.fieldLabel.length){
13788             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13789         }
13790
13791         var listHeight = this.touchViewListGroup.getHeight();
13792         
13793         var _this = this;
13794         
13795         if(firstChecked && listHeight > bodyHeight){
13796             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13797         }
13798         
13799     },
13800     
13801     onTouchViewLoadException : function()
13802     {
13803         this.hideTouchView();
13804     },
13805     
13806     onTouchViewEmptyResults : function()
13807     {
13808         this.clearTouchView();
13809         
13810         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13811         
13812         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13813         
13814     },
13815     
13816     clearTouchView : function()
13817     {
13818         this.touchViewListGroup.dom.innerHTML = '';
13819     },
13820     
13821     onTouchViewClick : function(e, el, o)
13822     {
13823         e.preventDefault();
13824         
13825         var row = o.row;
13826         var rowIndex = o.rowIndex;
13827         
13828         var r = this.store.getAt(rowIndex);
13829         
13830         if(!this.multiple){
13831             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13832                 c.dom.removeAttribute('checked');
13833             }, this);
13834             
13835             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13836         
13837             this.setFromData(r.data);
13838             
13839             var close = this.closeTriggerEl();
13840         
13841             if(close){
13842                 close.show();
13843             }
13844
13845             this.hideTouchView();
13846             
13847             this.fireEvent('select', this, r, rowIndex);
13848             
13849             return;
13850         }
13851         
13852         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13853             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13854             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13855             return;
13856         }
13857         
13858         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13859         this.addItem(r.data);
13860         this.tickItems.push(r.data);
13861         
13862     }
13863     
13864
13865     /** 
13866     * @cfg {Boolean} grow 
13867     * @hide 
13868     */
13869     /** 
13870     * @cfg {Number} growMin 
13871     * @hide 
13872     */
13873     /** 
13874     * @cfg {Number} growMax 
13875     * @hide 
13876     */
13877     /**
13878      * @hide
13879      * @method autoSize
13880      */
13881 });
13882
13883 Roo.apply(Roo.bootstrap.ComboBox,  {
13884     
13885     header : {
13886         tag: 'div',
13887         cls: 'modal-header',
13888         cn: [
13889             {
13890                 tag: 'h4',
13891                 cls: 'modal-title'
13892             }
13893         ]
13894     },
13895     
13896     body : {
13897         tag: 'div',
13898         cls: 'modal-body',
13899         cn: [
13900             {
13901                 tag: 'ul',
13902                 cls: 'list-group'
13903             }
13904         ]
13905     },
13906     
13907     listItemRadio : {
13908         tag: 'li',
13909         cls: 'list-group-item',
13910         cn: [
13911             {
13912                 tag: 'span',
13913                 cls: 'roo-combobox-list-group-item-value'
13914             },
13915             {
13916                 tag: 'div',
13917                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13918                 cn: [
13919                     {
13920                         tag: 'input',
13921                         type: 'radio'
13922                     },
13923                     {
13924                         tag: 'label'
13925                     }
13926                 ]
13927             }
13928         ]
13929     },
13930     
13931     listItemCheckbox : {
13932         tag: 'li',
13933         cls: 'list-group-item',
13934         cn: [
13935             {
13936                 tag: 'span',
13937                 cls: 'roo-combobox-list-group-item-value'
13938             },
13939             {
13940                 tag: 'div',
13941                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13942                 cn: [
13943                     {
13944                         tag: 'input',
13945                         type: 'checkbox'
13946                     },
13947                     {
13948                         tag: 'label'
13949                     }
13950                 ]
13951             }
13952         ]
13953     },
13954     
13955     emptyResult : {
13956         tag: 'div',
13957         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13958     },
13959     
13960     footer : {
13961         tag: 'div',
13962         cls: 'modal-footer',
13963         cn: [
13964             {
13965                 tag: 'div',
13966                 cls: 'row',
13967                 cn: [
13968                     {
13969                         tag: 'div',
13970                         cls: 'col-xs-6 text-left',
13971                         cn: {
13972                             tag: 'button',
13973                             cls: 'btn btn-danger roo-touch-view-cancel',
13974                             html: 'Cancel'
13975                         }
13976                     },
13977                     {
13978                         tag: 'div',
13979                         cls: 'col-xs-6 text-right',
13980                         cn: {
13981                             tag: 'button',
13982                             cls: 'btn btn-success roo-touch-view-ok',
13983                             html: 'OK'
13984                         }
13985                     }
13986                 ]
13987             }
13988         ]
13989         
13990     }
13991 });
13992
13993 Roo.apply(Roo.bootstrap.ComboBox,  {
13994     
13995     touchViewTemplate : {
13996         tag: 'div',
13997         cls: 'modal fade roo-combobox-touch-view',
13998         cn: [
13999             {
14000                 tag: 'div',
14001                 cls: 'modal-dialog',
14002                 style : 'position:fixed', // we have to fix position....
14003                 cn: [
14004                     {
14005                         tag: 'div',
14006                         cls: 'modal-content',
14007                         cn: [
14008                             Roo.bootstrap.ComboBox.header,
14009                             Roo.bootstrap.ComboBox.body,
14010                             Roo.bootstrap.ComboBox.footer
14011                         ]
14012                     }
14013                 ]
14014             }
14015         ]
14016     }
14017 });/*
14018  * Based on:
14019  * Ext JS Library 1.1.1
14020  * Copyright(c) 2006-2007, Ext JS, LLC.
14021  *
14022  * Originally Released Under LGPL - original licence link has changed is not relivant.
14023  *
14024  * Fork - LGPL
14025  * <script type="text/javascript">
14026  */
14027
14028 /**
14029  * @class Roo.View
14030  * @extends Roo.util.Observable
14031  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14032  * This class also supports single and multi selection modes. <br>
14033  * Create a data model bound view:
14034  <pre><code>
14035  var store = new Roo.data.Store(...);
14036
14037  var view = new Roo.View({
14038     el : "my-element",
14039     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14040  
14041     singleSelect: true,
14042     selectedClass: "ydataview-selected",
14043     store: store
14044  });
14045
14046  // listen for node click?
14047  view.on("click", function(vw, index, node, e){
14048  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14049  });
14050
14051  // load XML data
14052  dataModel.load("foobar.xml");
14053  </code></pre>
14054  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14055  * <br><br>
14056  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14057  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14058  * 
14059  * Note: old style constructor is still suported (container, template, config)
14060  * 
14061  * @constructor
14062  * Create a new View
14063  * @param {Object} config The config object
14064  * 
14065  */
14066 Roo.View = function(config, depreciated_tpl, depreciated_config){
14067     
14068     this.parent = false;
14069     
14070     if (typeof(depreciated_tpl) == 'undefined') {
14071         // new way.. - universal constructor.
14072         Roo.apply(this, config);
14073         this.el  = Roo.get(this.el);
14074     } else {
14075         // old format..
14076         this.el  = Roo.get(config);
14077         this.tpl = depreciated_tpl;
14078         Roo.apply(this, depreciated_config);
14079     }
14080     this.wrapEl  = this.el.wrap().wrap();
14081     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14082     
14083     
14084     if(typeof(this.tpl) == "string"){
14085         this.tpl = new Roo.Template(this.tpl);
14086     } else {
14087         // support xtype ctors..
14088         this.tpl = new Roo.factory(this.tpl, Roo);
14089     }
14090     
14091     
14092     this.tpl.compile();
14093     
14094     /** @private */
14095     this.addEvents({
14096         /**
14097          * @event beforeclick
14098          * Fires before a click is processed. Returns false to cancel the default action.
14099          * @param {Roo.View} this
14100          * @param {Number} index The index of the target node
14101          * @param {HTMLElement} node The target node
14102          * @param {Roo.EventObject} e The raw event object
14103          */
14104             "beforeclick" : true,
14105         /**
14106          * @event click
14107          * Fires when a template node is clicked.
14108          * @param {Roo.View} this
14109          * @param {Number} index The index of the target node
14110          * @param {HTMLElement} node The target node
14111          * @param {Roo.EventObject} e The raw event object
14112          */
14113             "click" : true,
14114         /**
14115          * @event dblclick
14116          * Fires when a template node is double clicked.
14117          * @param {Roo.View} this
14118          * @param {Number} index The index of the target node
14119          * @param {HTMLElement} node The target node
14120          * @param {Roo.EventObject} e The raw event object
14121          */
14122             "dblclick" : true,
14123         /**
14124          * @event contextmenu
14125          * Fires when a template node is right clicked.
14126          * @param {Roo.View} this
14127          * @param {Number} index The index of the target node
14128          * @param {HTMLElement} node The target node
14129          * @param {Roo.EventObject} e The raw event object
14130          */
14131             "contextmenu" : true,
14132         /**
14133          * @event selectionchange
14134          * Fires when the selected nodes change.
14135          * @param {Roo.View} this
14136          * @param {Array} selections Array of the selected nodes
14137          */
14138             "selectionchange" : true,
14139     
14140         /**
14141          * @event beforeselect
14142          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14143          * @param {Roo.View} this
14144          * @param {HTMLElement} node The node to be selected
14145          * @param {Array} selections Array of currently selected nodes
14146          */
14147             "beforeselect" : true,
14148         /**
14149          * @event preparedata
14150          * Fires on every row to render, to allow you to change the data.
14151          * @param {Roo.View} this
14152          * @param {Object} data to be rendered (change this)
14153          */
14154           "preparedata" : true
14155           
14156           
14157         });
14158
14159
14160
14161     this.el.on({
14162         "click": this.onClick,
14163         "dblclick": this.onDblClick,
14164         "contextmenu": this.onContextMenu,
14165         scope:this
14166     });
14167
14168     this.selections = [];
14169     this.nodes = [];
14170     this.cmp = new Roo.CompositeElementLite([]);
14171     if(this.store){
14172         this.store = Roo.factory(this.store, Roo.data);
14173         this.setStore(this.store, true);
14174     }
14175     
14176     if ( this.footer && this.footer.xtype) {
14177            
14178          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14179         
14180         this.footer.dataSource = this.store;
14181         this.footer.container = fctr;
14182         this.footer = Roo.factory(this.footer, Roo);
14183         fctr.insertFirst(this.el);
14184         
14185         // this is a bit insane - as the paging toolbar seems to detach the el..
14186 //        dom.parentNode.parentNode.parentNode
14187          // they get detached?
14188     }
14189     
14190     
14191     Roo.View.superclass.constructor.call(this);
14192     
14193     
14194 };
14195
14196 Roo.extend(Roo.View, Roo.util.Observable, {
14197     
14198      /**
14199      * @cfg {Roo.data.Store} store Data store to load data from.
14200      */
14201     store : false,
14202     
14203     /**
14204      * @cfg {String|Roo.Element} el The container element.
14205      */
14206     el : '',
14207     
14208     /**
14209      * @cfg {String|Roo.Template} tpl The template used by this View 
14210      */
14211     tpl : false,
14212     /**
14213      * @cfg {String} dataName the named area of the template to use as the data area
14214      *                          Works with domtemplates roo-name="name"
14215      */
14216     dataName: false,
14217     /**
14218      * @cfg {String} selectedClass The css class to add to selected nodes
14219      */
14220     selectedClass : "x-view-selected",
14221      /**
14222      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14223      */
14224     emptyText : "",
14225     
14226     /**
14227      * @cfg {String} text to display on mask (default Loading)
14228      */
14229     mask : false,
14230     /**
14231      * @cfg {Boolean} multiSelect Allow multiple selection
14232      */
14233     multiSelect : false,
14234     /**
14235      * @cfg {Boolean} singleSelect Allow single selection
14236      */
14237     singleSelect:  false,
14238     
14239     /**
14240      * @cfg {Boolean} toggleSelect - selecting 
14241      */
14242     toggleSelect : false,
14243     
14244     /**
14245      * @cfg {Boolean} tickable - selecting 
14246      */
14247     tickable : false,
14248     
14249     /**
14250      * Returns the element this view is bound to.
14251      * @return {Roo.Element}
14252      */
14253     getEl : function(){
14254         return this.wrapEl;
14255     },
14256     
14257     
14258
14259     /**
14260      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14261      */
14262     refresh : function(){
14263         //Roo.log('refresh');
14264         var t = this.tpl;
14265         
14266         // if we are using something like 'domtemplate', then
14267         // the what gets used is:
14268         // t.applySubtemplate(NAME, data, wrapping data..)
14269         // the outer template then get' applied with
14270         //     the store 'extra data'
14271         // and the body get's added to the
14272         //      roo-name="data" node?
14273         //      <span class='roo-tpl-{name}'></span> ?????
14274         
14275         
14276         
14277         this.clearSelections();
14278         this.el.update("");
14279         var html = [];
14280         var records = this.store.getRange();
14281         if(records.length < 1) {
14282             
14283             // is this valid??  = should it render a template??
14284             
14285             this.el.update(this.emptyText);
14286             return;
14287         }
14288         var el = this.el;
14289         if (this.dataName) {
14290             this.el.update(t.apply(this.store.meta)); //????
14291             el = this.el.child('.roo-tpl-' + this.dataName);
14292         }
14293         
14294         for(var i = 0, len = records.length; i < len; i++){
14295             var data = this.prepareData(records[i].data, i, records[i]);
14296             this.fireEvent("preparedata", this, data, i, records[i]);
14297             
14298             var d = Roo.apply({}, data);
14299             
14300             if(this.tickable){
14301                 Roo.apply(d, {'roo-id' : Roo.id()});
14302                 
14303                 var _this = this;
14304             
14305                 Roo.each(this.parent.item, function(item){
14306                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14307                         return;
14308                     }
14309                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14310                 });
14311             }
14312             
14313             html[html.length] = Roo.util.Format.trim(
14314                 this.dataName ?
14315                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14316                     t.apply(d)
14317             );
14318         }
14319         
14320         
14321         
14322         el.update(html.join(""));
14323         this.nodes = el.dom.childNodes;
14324         this.updateIndexes(0);
14325     },
14326     
14327
14328     /**
14329      * Function to override to reformat the data that is sent to
14330      * the template for each node.
14331      * DEPRICATED - use the preparedata event handler.
14332      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14333      * a JSON object for an UpdateManager bound view).
14334      */
14335     prepareData : function(data, index, record)
14336     {
14337         this.fireEvent("preparedata", this, data, index, record);
14338         return data;
14339     },
14340
14341     onUpdate : function(ds, record){
14342         // Roo.log('on update');   
14343         this.clearSelections();
14344         var index = this.store.indexOf(record);
14345         var n = this.nodes[index];
14346         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14347         n.parentNode.removeChild(n);
14348         this.updateIndexes(index, index);
14349     },
14350
14351     
14352     
14353 // --------- FIXME     
14354     onAdd : function(ds, records, index)
14355     {
14356         //Roo.log(['on Add', ds, records, index] );        
14357         this.clearSelections();
14358         if(this.nodes.length == 0){
14359             this.refresh();
14360             return;
14361         }
14362         var n = this.nodes[index];
14363         for(var i = 0, len = records.length; i < len; i++){
14364             var d = this.prepareData(records[i].data, i, records[i]);
14365             if(n){
14366                 this.tpl.insertBefore(n, d);
14367             }else{
14368                 
14369                 this.tpl.append(this.el, d);
14370             }
14371         }
14372         this.updateIndexes(index);
14373     },
14374
14375     onRemove : function(ds, record, index){
14376        // Roo.log('onRemove');
14377         this.clearSelections();
14378         var el = this.dataName  ?
14379             this.el.child('.roo-tpl-' + this.dataName) :
14380             this.el; 
14381         
14382         el.dom.removeChild(this.nodes[index]);
14383         this.updateIndexes(index);
14384     },
14385
14386     /**
14387      * Refresh an individual node.
14388      * @param {Number} index
14389      */
14390     refreshNode : function(index){
14391         this.onUpdate(this.store, this.store.getAt(index));
14392     },
14393
14394     updateIndexes : function(startIndex, endIndex){
14395         var ns = this.nodes;
14396         startIndex = startIndex || 0;
14397         endIndex = endIndex || ns.length - 1;
14398         for(var i = startIndex; i <= endIndex; i++){
14399             ns[i].nodeIndex = i;
14400         }
14401     },
14402
14403     /**
14404      * Changes the data store this view uses and refresh the view.
14405      * @param {Store} store
14406      */
14407     setStore : function(store, initial){
14408         if(!initial && this.store){
14409             this.store.un("datachanged", this.refresh);
14410             this.store.un("add", this.onAdd);
14411             this.store.un("remove", this.onRemove);
14412             this.store.un("update", this.onUpdate);
14413             this.store.un("clear", this.refresh);
14414             this.store.un("beforeload", this.onBeforeLoad);
14415             this.store.un("load", this.onLoad);
14416             this.store.un("loadexception", this.onLoad);
14417         }
14418         if(store){
14419           
14420             store.on("datachanged", this.refresh, this);
14421             store.on("add", this.onAdd, this);
14422             store.on("remove", this.onRemove, this);
14423             store.on("update", this.onUpdate, this);
14424             store.on("clear", this.refresh, this);
14425             store.on("beforeload", this.onBeforeLoad, this);
14426             store.on("load", this.onLoad, this);
14427             store.on("loadexception", this.onLoad, this);
14428         }
14429         
14430         if(store){
14431             this.refresh();
14432         }
14433     },
14434     /**
14435      * onbeforeLoad - masks the loading area.
14436      *
14437      */
14438     onBeforeLoad : function(store,opts)
14439     {
14440          //Roo.log('onBeforeLoad');   
14441         if (!opts.add) {
14442             this.el.update("");
14443         }
14444         this.el.mask(this.mask ? this.mask : "Loading" ); 
14445     },
14446     onLoad : function ()
14447     {
14448         this.el.unmask();
14449     },
14450     
14451
14452     /**
14453      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14454      * @param {HTMLElement} node
14455      * @return {HTMLElement} The template node
14456      */
14457     findItemFromChild : function(node){
14458         var el = this.dataName  ?
14459             this.el.child('.roo-tpl-' + this.dataName,true) :
14460             this.el.dom; 
14461         
14462         if(!node || node.parentNode == el){
14463                     return node;
14464             }
14465             var p = node.parentNode;
14466             while(p && p != el){
14467             if(p.parentNode == el){
14468                 return p;
14469             }
14470             p = p.parentNode;
14471         }
14472             return null;
14473     },
14474
14475     /** @ignore */
14476     onClick : function(e){
14477         var item = this.findItemFromChild(e.getTarget());
14478         if(item){
14479             var index = this.indexOf(item);
14480             if(this.onItemClick(item, index, e) !== false){
14481                 this.fireEvent("click", this, index, item, e);
14482             }
14483         }else{
14484             this.clearSelections();
14485         }
14486     },
14487
14488     /** @ignore */
14489     onContextMenu : function(e){
14490         var item = this.findItemFromChild(e.getTarget());
14491         if(item){
14492             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14493         }
14494     },
14495
14496     /** @ignore */
14497     onDblClick : function(e){
14498         var item = this.findItemFromChild(e.getTarget());
14499         if(item){
14500             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14501         }
14502     },
14503
14504     onItemClick : function(item, index, e)
14505     {
14506         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14507             return false;
14508         }
14509         if (this.toggleSelect) {
14510             var m = this.isSelected(item) ? 'unselect' : 'select';
14511             //Roo.log(m);
14512             var _t = this;
14513             _t[m](item, true, false);
14514             return true;
14515         }
14516         if(this.multiSelect || this.singleSelect){
14517             if(this.multiSelect && e.shiftKey && this.lastSelection){
14518                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14519             }else{
14520                 this.select(item, this.multiSelect && e.ctrlKey);
14521                 this.lastSelection = item;
14522             }
14523             
14524             if(!this.tickable){
14525                 e.preventDefault();
14526             }
14527             
14528         }
14529         return true;
14530     },
14531
14532     /**
14533      * Get the number of selected nodes.
14534      * @return {Number}
14535      */
14536     getSelectionCount : function(){
14537         return this.selections.length;
14538     },
14539
14540     /**
14541      * Get the currently selected nodes.
14542      * @return {Array} An array of HTMLElements
14543      */
14544     getSelectedNodes : function(){
14545         return this.selections;
14546     },
14547
14548     /**
14549      * Get the indexes of the selected nodes.
14550      * @return {Array}
14551      */
14552     getSelectedIndexes : function(){
14553         var indexes = [], s = this.selections;
14554         for(var i = 0, len = s.length; i < len; i++){
14555             indexes.push(s[i].nodeIndex);
14556         }
14557         return indexes;
14558     },
14559
14560     /**
14561      * Clear all selections
14562      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14563      */
14564     clearSelections : function(suppressEvent){
14565         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14566             this.cmp.elements = this.selections;
14567             this.cmp.removeClass(this.selectedClass);
14568             this.selections = [];
14569             if(!suppressEvent){
14570                 this.fireEvent("selectionchange", this, this.selections);
14571             }
14572         }
14573     },
14574
14575     /**
14576      * Returns true if the passed node is selected
14577      * @param {HTMLElement/Number} node The node or node index
14578      * @return {Boolean}
14579      */
14580     isSelected : function(node){
14581         var s = this.selections;
14582         if(s.length < 1){
14583             return false;
14584         }
14585         node = this.getNode(node);
14586         return s.indexOf(node) !== -1;
14587     },
14588
14589     /**
14590      * Selects nodes.
14591      * @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
14592      * @param {Boolean} keepExisting (optional) true to keep existing selections
14593      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14594      */
14595     select : function(nodeInfo, keepExisting, suppressEvent){
14596         if(nodeInfo instanceof Array){
14597             if(!keepExisting){
14598                 this.clearSelections(true);
14599             }
14600             for(var i = 0, len = nodeInfo.length; i < len; i++){
14601                 this.select(nodeInfo[i], true, true);
14602             }
14603             return;
14604         } 
14605         var node = this.getNode(nodeInfo);
14606         if(!node || this.isSelected(node)){
14607             return; // already selected.
14608         }
14609         if(!keepExisting){
14610             this.clearSelections(true);
14611         }
14612         
14613         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14614             Roo.fly(node).addClass(this.selectedClass);
14615             this.selections.push(node);
14616             if(!suppressEvent){
14617                 this.fireEvent("selectionchange", this, this.selections);
14618             }
14619         }
14620         
14621         
14622     },
14623       /**
14624      * Unselects nodes.
14625      * @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
14626      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14627      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14628      */
14629     unselect : function(nodeInfo, keepExisting, suppressEvent)
14630     {
14631         if(nodeInfo instanceof Array){
14632             Roo.each(this.selections, function(s) {
14633                 this.unselect(s, nodeInfo);
14634             }, this);
14635             return;
14636         }
14637         var node = this.getNode(nodeInfo);
14638         if(!node || !this.isSelected(node)){
14639             //Roo.log("not selected");
14640             return; // not selected.
14641         }
14642         // fireevent???
14643         var ns = [];
14644         Roo.each(this.selections, function(s) {
14645             if (s == node ) {
14646                 Roo.fly(node).removeClass(this.selectedClass);
14647
14648                 return;
14649             }
14650             ns.push(s);
14651         },this);
14652         
14653         this.selections= ns;
14654         this.fireEvent("selectionchange", this, this.selections);
14655     },
14656
14657     /**
14658      * Gets a template node.
14659      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14660      * @return {HTMLElement} The node or null if it wasn't found
14661      */
14662     getNode : function(nodeInfo){
14663         if(typeof nodeInfo == "string"){
14664             return document.getElementById(nodeInfo);
14665         }else if(typeof nodeInfo == "number"){
14666             return this.nodes[nodeInfo];
14667         }
14668         return nodeInfo;
14669     },
14670
14671     /**
14672      * Gets a range template nodes.
14673      * @param {Number} startIndex
14674      * @param {Number} endIndex
14675      * @return {Array} An array of nodes
14676      */
14677     getNodes : function(start, end){
14678         var ns = this.nodes;
14679         start = start || 0;
14680         end = typeof end == "undefined" ? ns.length - 1 : end;
14681         var nodes = [];
14682         if(start <= end){
14683             for(var i = start; i <= end; i++){
14684                 nodes.push(ns[i]);
14685             }
14686         } else{
14687             for(var i = start; i >= end; i--){
14688                 nodes.push(ns[i]);
14689             }
14690         }
14691         return nodes;
14692     },
14693
14694     /**
14695      * Finds the index of the passed node
14696      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14697      * @return {Number} The index of the node or -1
14698      */
14699     indexOf : function(node){
14700         node = this.getNode(node);
14701         if(typeof node.nodeIndex == "number"){
14702             return node.nodeIndex;
14703         }
14704         var ns = this.nodes;
14705         for(var i = 0, len = ns.length; i < len; i++){
14706             if(ns[i] == node){
14707                 return i;
14708             }
14709         }
14710         return -1;
14711     }
14712 });
14713 /*
14714  * - LGPL
14715  *
14716  * based on jquery fullcalendar
14717  * 
14718  */
14719
14720 Roo.bootstrap = Roo.bootstrap || {};
14721 /**
14722  * @class Roo.bootstrap.Calendar
14723  * @extends Roo.bootstrap.Component
14724  * Bootstrap Calendar class
14725  * @cfg {Boolean} loadMask (true|false) default false
14726  * @cfg {Object} header generate the user specific header of the calendar, default false
14727
14728  * @constructor
14729  * Create a new Container
14730  * @param {Object} config The config object
14731  */
14732
14733
14734
14735 Roo.bootstrap.Calendar = function(config){
14736     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14737      this.addEvents({
14738         /**
14739              * @event select
14740              * Fires when a date is selected
14741              * @param {DatePicker} this
14742              * @param {Date} date The selected date
14743              */
14744         'select': true,
14745         /**
14746              * @event monthchange
14747              * Fires when the displayed month changes 
14748              * @param {DatePicker} this
14749              * @param {Date} date The selected month
14750              */
14751         'monthchange': true,
14752         /**
14753              * @event evententer
14754              * Fires when mouse over an event
14755              * @param {Calendar} this
14756              * @param {event} Event
14757              */
14758         'evententer': true,
14759         /**
14760              * @event eventleave
14761              * Fires when the mouse leaves an
14762              * @param {Calendar} this
14763              * @param {event}
14764              */
14765         'eventleave': true,
14766         /**
14767              * @event eventclick
14768              * Fires when the mouse click an
14769              * @param {Calendar} this
14770              * @param {event}
14771              */
14772         'eventclick': true
14773         
14774     });
14775
14776 };
14777
14778 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14779     
14780      /**
14781      * @cfg {Number} startDay
14782      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14783      */
14784     startDay : 0,
14785     
14786     loadMask : false,
14787     
14788     header : false,
14789       
14790     getAutoCreate : function(){
14791         
14792         
14793         var fc_button = function(name, corner, style, content ) {
14794             return Roo.apply({},{
14795                 tag : 'span',
14796                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14797                          (corner.length ?
14798                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14799                             ''
14800                         ),
14801                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14802                 unselectable: 'on'
14803             });
14804         };
14805         
14806         var header = {};
14807         
14808         if(!this.header){
14809             header = {
14810                 tag : 'table',
14811                 cls : 'fc-header',
14812                 style : 'width:100%',
14813                 cn : [
14814                     {
14815                         tag: 'tr',
14816                         cn : [
14817                             {
14818                                 tag : 'td',
14819                                 cls : 'fc-header-left',
14820                                 cn : [
14821                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14822                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14823                                     { tag: 'span', cls: 'fc-header-space' },
14824                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14825
14826
14827                                 ]
14828                             },
14829
14830                             {
14831                                 tag : 'td',
14832                                 cls : 'fc-header-center',
14833                                 cn : [
14834                                     {
14835                                         tag: 'span',
14836                                         cls: 'fc-header-title',
14837                                         cn : {
14838                                             tag: 'H2',
14839                                             html : 'month / year'
14840                                         }
14841                                     }
14842
14843                                 ]
14844                             },
14845                             {
14846                                 tag : 'td',
14847                                 cls : 'fc-header-right',
14848                                 cn : [
14849                               /*      fc_button('month', 'left', '', 'month' ),
14850                                     fc_button('week', '', '', 'week' ),
14851                                     fc_button('day', 'right', '', 'day' )
14852                                 */    
14853
14854                                 ]
14855                             }
14856
14857                         ]
14858                     }
14859                 ]
14860             };
14861         }
14862         
14863         header = this.header;
14864         
14865        
14866         var cal_heads = function() {
14867             var ret = [];
14868             // fixme - handle this.
14869             
14870             for (var i =0; i < Date.dayNames.length; i++) {
14871                 var d = Date.dayNames[i];
14872                 ret.push({
14873                     tag: 'th',
14874                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14875                     html : d.substring(0,3)
14876                 });
14877                 
14878             }
14879             ret[0].cls += ' fc-first';
14880             ret[6].cls += ' fc-last';
14881             return ret;
14882         };
14883         var cal_cell = function(n) {
14884             return  {
14885                 tag: 'td',
14886                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14887                 cn : [
14888                     {
14889                         cn : [
14890                             {
14891                                 cls: 'fc-day-number',
14892                                 html: 'D'
14893                             },
14894                             {
14895                                 cls: 'fc-day-content',
14896                              
14897                                 cn : [
14898                                      {
14899                                         style: 'position: relative;' // height: 17px;
14900                                     }
14901                                 ]
14902                             }
14903                             
14904                             
14905                         ]
14906                     }
14907                 ]
14908                 
14909             }
14910         };
14911         var cal_rows = function() {
14912             
14913             var ret = [];
14914             for (var r = 0; r < 6; r++) {
14915                 var row= {
14916                     tag : 'tr',
14917                     cls : 'fc-week',
14918                     cn : []
14919                 };
14920                 
14921                 for (var i =0; i < Date.dayNames.length; i++) {
14922                     var d = Date.dayNames[i];
14923                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14924
14925                 }
14926                 row.cn[0].cls+=' fc-first';
14927                 row.cn[0].cn[0].style = 'min-height:90px';
14928                 row.cn[6].cls+=' fc-last';
14929                 ret.push(row);
14930                 
14931             }
14932             ret[0].cls += ' fc-first';
14933             ret[4].cls += ' fc-prev-last';
14934             ret[5].cls += ' fc-last';
14935             return ret;
14936             
14937         };
14938         
14939         var cal_table = {
14940             tag: 'table',
14941             cls: 'fc-border-separate',
14942             style : 'width:100%',
14943             cellspacing  : 0,
14944             cn : [
14945                 { 
14946                     tag: 'thead',
14947                     cn : [
14948                         { 
14949                             tag: 'tr',
14950                             cls : 'fc-first fc-last',
14951                             cn : cal_heads()
14952                         }
14953                     ]
14954                 },
14955                 { 
14956                     tag: 'tbody',
14957                     cn : cal_rows()
14958                 }
14959                   
14960             ]
14961         };
14962          
14963          var cfg = {
14964             cls : 'fc fc-ltr',
14965             cn : [
14966                 header,
14967                 {
14968                     cls : 'fc-content',
14969                     style : "position: relative;",
14970                     cn : [
14971                         {
14972                             cls : 'fc-view fc-view-month fc-grid',
14973                             style : 'position: relative',
14974                             unselectable : 'on',
14975                             cn : [
14976                                 {
14977                                     cls : 'fc-event-container',
14978                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14979                                 },
14980                                 cal_table
14981                             ]
14982                         }
14983                     ]
14984     
14985                 }
14986            ] 
14987             
14988         };
14989         
14990          
14991         
14992         return cfg;
14993     },
14994     
14995     
14996     initEvents : function()
14997     {
14998         if(!this.store){
14999             throw "can not find store for calendar";
15000         }
15001         
15002         var mark = {
15003             tag: "div",
15004             cls:"x-dlg-mask",
15005             style: "text-align:center",
15006             cn: [
15007                 {
15008                     tag: "div",
15009                     style: "background-color:white;width:50%;margin:250 auto",
15010                     cn: [
15011                         {
15012                             tag: "img",
15013                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15014                         },
15015                         {
15016                             tag: "span",
15017                             html: "Loading"
15018                         }
15019                         
15020                     ]
15021                 }
15022             ]
15023         };
15024         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15025         
15026         var size = this.el.select('.fc-content', true).first().getSize();
15027         this.maskEl.setSize(size.width, size.height);
15028         this.maskEl.enableDisplayMode("block");
15029         if(!this.loadMask){
15030             this.maskEl.hide();
15031         }
15032         
15033         this.store = Roo.factory(this.store, Roo.data);
15034         this.store.on('load', this.onLoad, this);
15035         this.store.on('beforeload', this.onBeforeLoad, this);
15036         
15037         this.resize();
15038         
15039         this.cells = this.el.select('.fc-day',true);
15040         //Roo.log(this.cells);
15041         this.textNodes = this.el.query('.fc-day-number');
15042         this.cells.addClassOnOver('fc-state-hover');
15043         
15044         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15045         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15046         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15047         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15048         
15049         this.on('monthchange', this.onMonthChange, this);
15050         
15051         this.update(new Date().clearTime());
15052     },
15053     
15054     resize : function() {
15055         var sz  = this.el.getSize();
15056         
15057         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15058         this.el.select('.fc-day-content div',true).setHeight(34);
15059     },
15060     
15061     
15062     // private
15063     showPrevMonth : function(e){
15064         this.update(this.activeDate.add("mo", -1));
15065     },
15066     showToday : function(e){
15067         this.update(new Date().clearTime());
15068     },
15069     // private
15070     showNextMonth : function(e){
15071         this.update(this.activeDate.add("mo", 1));
15072     },
15073
15074     // private
15075     showPrevYear : function(){
15076         this.update(this.activeDate.add("y", -1));
15077     },
15078
15079     // private
15080     showNextYear : function(){
15081         this.update(this.activeDate.add("y", 1));
15082     },
15083
15084     
15085    // private
15086     update : function(date)
15087     {
15088         var vd = this.activeDate;
15089         this.activeDate = date;
15090 //        if(vd && this.el){
15091 //            var t = date.getTime();
15092 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15093 //                Roo.log('using add remove');
15094 //                
15095 //                this.fireEvent('monthchange', this, date);
15096 //                
15097 //                this.cells.removeClass("fc-state-highlight");
15098 //                this.cells.each(function(c){
15099 //                   if(c.dateValue == t){
15100 //                       c.addClass("fc-state-highlight");
15101 //                       setTimeout(function(){
15102 //                            try{c.dom.firstChild.focus();}catch(e){}
15103 //                       }, 50);
15104 //                       return false;
15105 //                   }
15106 //                   return true;
15107 //                });
15108 //                return;
15109 //            }
15110 //        }
15111         
15112         var days = date.getDaysInMonth();
15113         
15114         var firstOfMonth = date.getFirstDateOfMonth();
15115         var startingPos = firstOfMonth.getDay()-this.startDay;
15116         
15117         if(startingPos < this.startDay){
15118             startingPos += 7;
15119         }
15120         
15121         var pm = date.add(Date.MONTH, -1);
15122         var prevStart = pm.getDaysInMonth()-startingPos;
15123 //        
15124         this.cells = this.el.select('.fc-day',true);
15125         this.textNodes = this.el.query('.fc-day-number');
15126         this.cells.addClassOnOver('fc-state-hover');
15127         
15128         var cells = this.cells.elements;
15129         var textEls = this.textNodes;
15130         
15131         Roo.each(cells, function(cell){
15132             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15133         });
15134         
15135         days += startingPos;
15136
15137         // convert everything to numbers so it's fast
15138         var day = 86400000;
15139         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15140         //Roo.log(d);
15141         //Roo.log(pm);
15142         //Roo.log(prevStart);
15143         
15144         var today = new Date().clearTime().getTime();
15145         var sel = date.clearTime().getTime();
15146         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15147         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15148         var ddMatch = this.disabledDatesRE;
15149         var ddText = this.disabledDatesText;
15150         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15151         var ddaysText = this.disabledDaysText;
15152         var format = this.format;
15153         
15154         var setCellClass = function(cal, cell){
15155             cell.row = 0;
15156             cell.events = [];
15157             cell.more = [];
15158             //Roo.log('set Cell Class');
15159             cell.title = "";
15160             var t = d.getTime();
15161             
15162             //Roo.log(d);
15163             
15164             cell.dateValue = t;
15165             if(t == today){
15166                 cell.className += " fc-today";
15167                 cell.className += " fc-state-highlight";
15168                 cell.title = cal.todayText;
15169             }
15170             if(t == sel){
15171                 // disable highlight in other month..
15172                 //cell.className += " fc-state-highlight";
15173                 
15174             }
15175             // disabling
15176             if(t < min) {
15177                 cell.className = " fc-state-disabled";
15178                 cell.title = cal.minText;
15179                 return;
15180             }
15181             if(t > max) {
15182                 cell.className = " fc-state-disabled";
15183                 cell.title = cal.maxText;
15184                 return;
15185             }
15186             if(ddays){
15187                 if(ddays.indexOf(d.getDay()) != -1){
15188                     cell.title = ddaysText;
15189                     cell.className = " fc-state-disabled";
15190                 }
15191             }
15192             if(ddMatch && format){
15193                 var fvalue = d.dateFormat(format);
15194                 if(ddMatch.test(fvalue)){
15195                     cell.title = ddText.replace("%0", fvalue);
15196                     cell.className = " fc-state-disabled";
15197                 }
15198             }
15199             
15200             if (!cell.initialClassName) {
15201                 cell.initialClassName = cell.dom.className;
15202             }
15203             
15204             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15205         };
15206
15207         var i = 0;
15208         
15209         for(; i < startingPos; i++) {
15210             textEls[i].innerHTML = (++prevStart);
15211             d.setDate(d.getDate()+1);
15212             
15213             cells[i].className = "fc-past fc-other-month";
15214             setCellClass(this, cells[i]);
15215         }
15216         
15217         var intDay = 0;
15218         
15219         for(; i < days; i++){
15220             intDay = i - startingPos + 1;
15221             textEls[i].innerHTML = (intDay);
15222             d.setDate(d.getDate()+1);
15223             
15224             cells[i].className = ''; // "x-date-active";
15225             setCellClass(this, cells[i]);
15226         }
15227         var extraDays = 0;
15228         
15229         for(; i < 42; i++) {
15230             textEls[i].innerHTML = (++extraDays);
15231             d.setDate(d.getDate()+1);
15232             
15233             cells[i].className = "fc-future fc-other-month";
15234             setCellClass(this, cells[i]);
15235         }
15236         
15237         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15238         
15239         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15240         
15241         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15242         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15243         
15244         if(totalRows != 6){
15245             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15246             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15247         }
15248         
15249         this.fireEvent('monthchange', this, date);
15250         
15251         
15252         /*
15253         if(!this.internalRender){
15254             var main = this.el.dom.firstChild;
15255             var w = main.offsetWidth;
15256             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15257             Roo.fly(main).setWidth(w);
15258             this.internalRender = true;
15259             // opera does not respect the auto grow header center column
15260             // then, after it gets a width opera refuses to recalculate
15261             // without a second pass
15262             if(Roo.isOpera && !this.secondPass){
15263                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15264                 this.secondPass = true;
15265                 this.update.defer(10, this, [date]);
15266             }
15267         }
15268         */
15269         
15270     },
15271     
15272     findCell : function(dt) {
15273         dt = dt.clearTime().getTime();
15274         var ret = false;
15275         this.cells.each(function(c){
15276             //Roo.log("check " +c.dateValue + '?=' + dt);
15277             if(c.dateValue == dt){
15278                 ret = c;
15279                 return false;
15280             }
15281             return true;
15282         });
15283         
15284         return ret;
15285     },
15286     
15287     findCells : function(ev) {
15288         var s = ev.start.clone().clearTime().getTime();
15289        // Roo.log(s);
15290         var e= ev.end.clone().clearTime().getTime();
15291        // Roo.log(e);
15292         var ret = [];
15293         this.cells.each(function(c){
15294              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15295             
15296             if(c.dateValue > e){
15297                 return ;
15298             }
15299             if(c.dateValue < s){
15300                 return ;
15301             }
15302             ret.push(c);
15303         });
15304         
15305         return ret;    
15306     },
15307     
15308 //    findBestRow: function(cells)
15309 //    {
15310 //        var ret = 0;
15311 //        
15312 //        for (var i =0 ; i < cells.length;i++) {
15313 //            ret  = Math.max(cells[i].rows || 0,ret);
15314 //        }
15315 //        return ret;
15316 //        
15317 //    },
15318     
15319     
15320     addItem : function(ev)
15321     {
15322         // look for vertical location slot in
15323         var cells = this.findCells(ev);
15324         
15325 //        ev.row = this.findBestRow(cells);
15326         
15327         // work out the location.
15328         
15329         var crow = false;
15330         var rows = [];
15331         for(var i =0; i < cells.length; i++) {
15332             
15333             cells[i].row = cells[0].row;
15334             
15335             if(i == 0){
15336                 cells[i].row = cells[i].row + 1;
15337             }
15338             
15339             if (!crow) {
15340                 crow = {
15341                     start : cells[i],
15342                     end :  cells[i]
15343                 };
15344                 continue;
15345             }
15346             if (crow.start.getY() == cells[i].getY()) {
15347                 // on same row.
15348                 crow.end = cells[i];
15349                 continue;
15350             }
15351             // different row.
15352             rows.push(crow);
15353             crow = {
15354                 start: cells[i],
15355                 end : cells[i]
15356             };
15357             
15358         }
15359         
15360         rows.push(crow);
15361         ev.els = [];
15362         ev.rows = rows;
15363         ev.cells = cells;
15364         
15365         cells[0].events.push(ev);
15366         
15367         this.calevents.push(ev);
15368     },
15369     
15370     clearEvents: function() {
15371         
15372         if(!this.calevents){
15373             return;
15374         }
15375         
15376         Roo.each(this.cells.elements, function(c){
15377             c.row = 0;
15378             c.events = [];
15379             c.more = [];
15380         });
15381         
15382         Roo.each(this.calevents, function(e) {
15383             Roo.each(e.els, function(el) {
15384                 el.un('mouseenter' ,this.onEventEnter, this);
15385                 el.un('mouseleave' ,this.onEventLeave, this);
15386                 el.remove();
15387             },this);
15388         },this);
15389         
15390         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15391             e.remove();
15392         });
15393         
15394     },
15395     
15396     renderEvents: function()
15397     {   
15398         var _this = this;
15399         
15400         this.cells.each(function(c) {
15401             
15402             if(c.row < 5){
15403                 return;
15404             }
15405             
15406             var ev = c.events;
15407             
15408             var r = 4;
15409             if(c.row != c.events.length){
15410                 r = 4 - (4 - (c.row - c.events.length));
15411             }
15412             
15413             c.events = ev.slice(0, r);
15414             c.more = ev.slice(r);
15415             
15416             if(c.more.length && c.more.length == 1){
15417                 c.events.push(c.more.pop());
15418             }
15419             
15420             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15421             
15422         });
15423             
15424         this.cells.each(function(c) {
15425             
15426             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15427             
15428             
15429             for (var e = 0; e < c.events.length; e++){
15430                 var ev = c.events[e];
15431                 var rows = ev.rows;
15432                 
15433                 for(var i = 0; i < rows.length; i++) {
15434                 
15435                     // how many rows should it span..
15436
15437                     var  cfg = {
15438                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15439                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15440
15441                         unselectable : "on",
15442                         cn : [
15443                             {
15444                                 cls: 'fc-event-inner',
15445                                 cn : [
15446     //                                {
15447     //                                  tag:'span',
15448     //                                  cls: 'fc-event-time',
15449     //                                  html : cells.length > 1 ? '' : ev.time
15450     //                                },
15451                                     {
15452                                       tag:'span',
15453                                       cls: 'fc-event-title',
15454                                       html : String.format('{0}', ev.title)
15455                                     }
15456
15457
15458                                 ]
15459                             },
15460                             {
15461                                 cls: 'ui-resizable-handle ui-resizable-e',
15462                                 html : '&nbsp;&nbsp;&nbsp'
15463                             }
15464
15465                         ]
15466                     };
15467
15468                     if (i == 0) {
15469                         cfg.cls += ' fc-event-start';
15470                     }
15471                     if ((i+1) == rows.length) {
15472                         cfg.cls += ' fc-event-end';
15473                     }
15474
15475                     var ctr = _this.el.select('.fc-event-container',true).first();
15476                     var cg = ctr.createChild(cfg);
15477
15478                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15479                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15480
15481                     var r = (c.more.length) ? 1 : 0;
15482                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15483                     cg.setWidth(ebox.right - sbox.x -2);
15484
15485                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15486                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15487                     cg.on('click', _this.onEventClick, _this, ev);
15488
15489                     ev.els.push(cg);
15490                     
15491                 }
15492                 
15493             }
15494             
15495             
15496             if(c.more.length){
15497                 var  cfg = {
15498                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15499                     style : 'position: absolute',
15500                     unselectable : "on",
15501                     cn : [
15502                         {
15503                             cls: 'fc-event-inner',
15504                             cn : [
15505                                 {
15506                                   tag:'span',
15507                                   cls: 'fc-event-title',
15508                                   html : 'More'
15509                                 }
15510
15511
15512                             ]
15513                         },
15514                         {
15515                             cls: 'ui-resizable-handle ui-resizable-e',
15516                             html : '&nbsp;&nbsp;&nbsp'
15517                         }
15518
15519                     ]
15520                 };
15521
15522                 var ctr = _this.el.select('.fc-event-container',true).first();
15523                 var cg = ctr.createChild(cfg);
15524
15525                 var sbox = c.select('.fc-day-content',true).first().getBox();
15526                 var ebox = c.select('.fc-day-content',true).first().getBox();
15527                 //Roo.log(cg);
15528                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15529                 cg.setWidth(ebox.right - sbox.x -2);
15530
15531                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15532                 
15533             }
15534             
15535         });
15536         
15537         
15538         
15539     },
15540     
15541     onEventEnter: function (e, el,event,d) {
15542         this.fireEvent('evententer', this, el, event);
15543     },
15544     
15545     onEventLeave: function (e, el,event,d) {
15546         this.fireEvent('eventleave', this, el, event);
15547     },
15548     
15549     onEventClick: function (e, el,event,d) {
15550         this.fireEvent('eventclick', this, el, event);
15551     },
15552     
15553     onMonthChange: function () {
15554         this.store.load();
15555     },
15556     
15557     onMoreEventClick: function(e, el, more)
15558     {
15559         var _this = this;
15560         
15561         this.calpopover.placement = 'right';
15562         this.calpopover.setTitle('More');
15563         
15564         this.calpopover.setContent('');
15565         
15566         var ctr = this.calpopover.el.select('.popover-content', true).first();
15567         
15568         Roo.each(more, function(m){
15569             var cfg = {
15570                 cls : 'fc-event-hori fc-event-draggable',
15571                 html : m.title
15572             };
15573             var cg = ctr.createChild(cfg);
15574             
15575             cg.on('click', _this.onEventClick, _this, m);
15576         });
15577         
15578         this.calpopover.show(el);
15579         
15580         
15581     },
15582     
15583     onLoad: function () 
15584     {   
15585         this.calevents = [];
15586         var cal = this;
15587         
15588         if(this.store.getCount() > 0){
15589             this.store.data.each(function(d){
15590                cal.addItem({
15591                     id : d.data.id,
15592                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15593                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15594                     time : d.data.start_time,
15595                     title : d.data.title,
15596                     description : d.data.description,
15597                     venue : d.data.venue
15598                 });
15599             });
15600         }
15601         
15602         this.renderEvents();
15603         
15604         if(this.calevents.length && this.loadMask){
15605             this.maskEl.hide();
15606         }
15607     },
15608     
15609     onBeforeLoad: function()
15610     {
15611         this.clearEvents();
15612         if(this.loadMask){
15613             this.maskEl.show();
15614         }
15615     }
15616 });
15617
15618  
15619  /*
15620  * - LGPL
15621  *
15622  * element
15623  * 
15624  */
15625
15626 /**
15627  * @class Roo.bootstrap.Popover
15628  * @extends Roo.bootstrap.Component
15629  * Bootstrap Popover class
15630  * @cfg {String} html contents of the popover   (or false to use children..)
15631  * @cfg {String} title of popover (or false to hide)
15632  * @cfg {String} placement how it is placed
15633  * @cfg {String} trigger click || hover (or false to trigger manually)
15634  * @cfg {String} over what (parent or false to trigger manually.)
15635  * @cfg {Number} delay - delay before showing
15636  
15637  * @constructor
15638  * Create a new Popover
15639  * @param {Object} config The config object
15640  */
15641
15642 Roo.bootstrap.Popover = function(config){
15643     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15644 };
15645
15646 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15647     
15648     title: 'Fill in a title',
15649     html: false,
15650     
15651     placement : 'right',
15652     trigger : 'hover', // hover
15653     
15654     delay : 0,
15655     
15656     over: 'parent',
15657     
15658     can_build_overlaid : false,
15659     
15660     getChildContainer : function()
15661     {
15662         return this.el.select('.popover-content',true).first();
15663     },
15664     
15665     getAutoCreate : function(){
15666          
15667         var cfg = {
15668            cls : 'popover roo-dynamic',
15669            style: 'display:block',
15670            cn : [
15671                 {
15672                     cls : 'arrow'
15673                 },
15674                 {
15675                     cls : 'popover-inner',
15676                     cn : [
15677                         {
15678                             tag: 'h3',
15679                             cls: 'popover-title',
15680                             html : this.title
15681                         },
15682                         {
15683                             cls : 'popover-content',
15684                             html : this.html
15685                         }
15686                     ]
15687                     
15688                 }
15689            ]
15690         };
15691         
15692         return cfg;
15693     },
15694     setTitle: function(str)
15695     {
15696         this.title = str;
15697         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15698     },
15699     setContent: function(str)
15700     {
15701         this.html = str;
15702         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15703     },
15704     // as it get's added to the bottom of the page.
15705     onRender : function(ct, position)
15706     {
15707         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15708         if(!this.el){
15709             var cfg = Roo.apply({},  this.getAutoCreate());
15710             cfg.id = Roo.id();
15711             
15712             if (this.cls) {
15713                 cfg.cls += ' ' + this.cls;
15714             }
15715             if (this.style) {
15716                 cfg.style = this.style;
15717             }
15718             //Roo.log("adding to ");
15719             this.el = Roo.get(document.body).createChild(cfg, position);
15720 //            Roo.log(this.el);
15721         }
15722         this.initEvents();
15723     },
15724     
15725     initEvents : function()
15726     {
15727         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15728         this.el.enableDisplayMode('block');
15729         this.el.hide();
15730         if (this.over === false) {
15731             return; 
15732         }
15733         if (this.triggers === false) {
15734             return;
15735         }
15736         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15737         var triggers = this.trigger ? this.trigger.split(' ') : [];
15738         Roo.each(triggers, function(trigger) {
15739         
15740             if (trigger == 'click') {
15741                 on_el.on('click', this.toggle, this);
15742             } else if (trigger != 'manual') {
15743                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15744                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15745       
15746                 on_el.on(eventIn  ,this.enter, this);
15747                 on_el.on(eventOut, this.leave, this);
15748             }
15749         }, this);
15750         
15751     },
15752     
15753     
15754     // private
15755     timeout : null,
15756     hoverState : null,
15757     
15758     toggle : function () {
15759         this.hoverState == 'in' ? this.leave() : this.enter();
15760     },
15761     
15762     enter : function () {
15763        
15764     
15765         clearTimeout(this.timeout);
15766     
15767         this.hoverState = 'in';
15768     
15769         if (!this.delay || !this.delay.show) {
15770             this.show();
15771             return;
15772         }
15773         var _t = this;
15774         this.timeout = setTimeout(function () {
15775             if (_t.hoverState == 'in') {
15776                 _t.show();
15777             }
15778         }, this.delay.show)
15779     },
15780     leave : function() {
15781         clearTimeout(this.timeout);
15782     
15783         this.hoverState = 'out';
15784     
15785         if (!this.delay || !this.delay.hide) {
15786             this.hide();
15787             return;
15788         }
15789         var _t = this;
15790         this.timeout = setTimeout(function () {
15791             if (_t.hoverState == 'out') {
15792                 _t.hide();
15793             }
15794         }, this.delay.hide)
15795     },
15796     
15797     show : function (on_el)
15798     {
15799         if (!on_el) {
15800             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15801         }
15802         // set content.
15803         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15804         if (this.html !== false) {
15805             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15806         }
15807         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15808         if (!this.title.length) {
15809             this.el.select('.popover-title',true).hide();
15810         }
15811         
15812         var placement = typeof this.placement == 'function' ?
15813             this.placement.call(this, this.el, on_el) :
15814             this.placement;
15815             
15816         var autoToken = /\s?auto?\s?/i;
15817         var autoPlace = autoToken.test(placement);
15818         if (autoPlace) {
15819             placement = placement.replace(autoToken, '') || 'top';
15820         }
15821         
15822         //this.el.detach()
15823         //this.el.setXY([0,0]);
15824         this.el.show();
15825         this.el.dom.style.display='block';
15826         this.el.addClass(placement);
15827         
15828         //this.el.appendTo(on_el);
15829         
15830         var p = this.getPosition();
15831         var box = this.el.getBox();
15832         
15833         if (autoPlace) {
15834             // fixme..
15835         }
15836         var align = Roo.bootstrap.Popover.alignment[placement];
15837         this.el.alignTo(on_el, align[0],align[1]);
15838         //var arrow = this.el.select('.arrow',true).first();
15839         //arrow.set(align[2], 
15840         
15841         this.el.addClass('in');
15842         
15843         
15844         if (this.el.hasClass('fade')) {
15845             // fade it?
15846         }
15847         
15848     },
15849     hide : function()
15850     {
15851         this.el.setXY([0,0]);
15852         this.el.removeClass('in');
15853         this.el.hide();
15854         this.hoverState = null;
15855         
15856     }
15857     
15858 });
15859
15860 Roo.bootstrap.Popover.alignment = {
15861     'left' : ['r-l', [-10,0], 'right'],
15862     'right' : ['l-r', [10,0], 'left'],
15863     'bottom' : ['t-b', [0,10], 'top'],
15864     'top' : [ 'b-t', [0,-10], 'bottom']
15865 };
15866
15867  /*
15868  * - LGPL
15869  *
15870  * Progress
15871  * 
15872  */
15873
15874 /**
15875  * @class Roo.bootstrap.Progress
15876  * @extends Roo.bootstrap.Component
15877  * Bootstrap Progress class
15878  * @cfg {Boolean} striped striped of the progress bar
15879  * @cfg {Boolean} active animated of the progress bar
15880  * 
15881  * 
15882  * @constructor
15883  * Create a new Progress
15884  * @param {Object} config The config object
15885  */
15886
15887 Roo.bootstrap.Progress = function(config){
15888     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15889 };
15890
15891 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15892     
15893     striped : false,
15894     active: false,
15895     
15896     getAutoCreate : function(){
15897         var cfg = {
15898             tag: 'div',
15899             cls: 'progress'
15900         };
15901         
15902         
15903         if(this.striped){
15904             cfg.cls += ' progress-striped';
15905         }
15906       
15907         if(this.active){
15908             cfg.cls += ' active';
15909         }
15910         
15911         
15912         return cfg;
15913     }
15914    
15915 });
15916
15917  
15918
15919  /*
15920  * - LGPL
15921  *
15922  * ProgressBar
15923  * 
15924  */
15925
15926 /**
15927  * @class Roo.bootstrap.ProgressBar
15928  * @extends Roo.bootstrap.Component
15929  * Bootstrap ProgressBar class
15930  * @cfg {Number} aria_valuenow aria-value now
15931  * @cfg {Number} aria_valuemin aria-value min
15932  * @cfg {Number} aria_valuemax aria-value max
15933  * @cfg {String} label label for the progress bar
15934  * @cfg {String} panel (success | info | warning | danger )
15935  * @cfg {String} role role of the progress bar
15936  * @cfg {String} sr_only text
15937  * 
15938  * 
15939  * @constructor
15940  * Create a new ProgressBar
15941  * @param {Object} config The config object
15942  */
15943
15944 Roo.bootstrap.ProgressBar = function(config){
15945     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15946 };
15947
15948 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15949     
15950     aria_valuenow : 0,
15951     aria_valuemin : 0,
15952     aria_valuemax : 100,
15953     label : false,
15954     panel : false,
15955     role : false,
15956     sr_only: false,
15957     
15958     getAutoCreate : function()
15959     {
15960         
15961         var cfg = {
15962             tag: 'div',
15963             cls: 'progress-bar',
15964             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15965         };
15966         
15967         if(this.sr_only){
15968             cfg.cn = {
15969                 tag: 'span',
15970                 cls: 'sr-only',
15971                 html: this.sr_only
15972             }
15973         }
15974         
15975         if(this.role){
15976             cfg.role = this.role;
15977         }
15978         
15979         if(this.aria_valuenow){
15980             cfg['aria-valuenow'] = this.aria_valuenow;
15981         }
15982         
15983         if(this.aria_valuemin){
15984             cfg['aria-valuemin'] = this.aria_valuemin;
15985         }
15986         
15987         if(this.aria_valuemax){
15988             cfg['aria-valuemax'] = this.aria_valuemax;
15989         }
15990         
15991         if(this.label && !this.sr_only){
15992             cfg.html = this.label;
15993         }
15994         
15995         if(this.panel){
15996             cfg.cls += ' progress-bar-' + this.panel;
15997         }
15998         
15999         return cfg;
16000     },
16001     
16002     update : function(aria_valuenow)
16003     {
16004         this.aria_valuenow = aria_valuenow;
16005         
16006         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16007     }
16008    
16009 });
16010
16011  
16012
16013  /*
16014  * - LGPL
16015  *
16016  * column
16017  * 
16018  */
16019
16020 /**
16021  * @class Roo.bootstrap.TabGroup
16022  * @extends Roo.bootstrap.Column
16023  * Bootstrap Column class
16024  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16025  * @cfg {Boolean} carousel true to make the group behave like a carousel
16026  * @cfg {Boolean} bullets show bullets for the panels
16027  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16028  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16029  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16030  * 
16031  * @constructor
16032  * Create a new TabGroup
16033  * @param {Object} config The config object
16034  */
16035
16036 Roo.bootstrap.TabGroup = function(config){
16037     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16038     if (!this.navId) {
16039         this.navId = Roo.id();
16040     }
16041     this.tabs = [];
16042     Roo.bootstrap.TabGroup.register(this);
16043     
16044 };
16045
16046 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16047     
16048     carousel : false,
16049     transition : false,
16050     bullets : 0,
16051     timer : 0,
16052     autoslide : false,
16053     slideFn : false,
16054     slideOnTouch : false,
16055     
16056     getAutoCreate : function()
16057     {
16058         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16059         
16060         cfg.cls += ' tab-content';
16061         
16062         if (this.carousel) {
16063             cfg.cls += ' carousel slide';
16064             
16065             cfg.cn = [{
16066                cls : 'carousel-inner'
16067             }];
16068         
16069             if(this.bullets  && !Roo.isTouch){
16070                 
16071                 var bullets = {
16072                     cls : 'carousel-bullets',
16073                     cn : []
16074                 };
16075                
16076                 if(this.bullets_cls){
16077                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16078                 }
16079                  /*
16080                 for (var i = 0; i < this.bullets; i++){
16081                     bullets.cn.push({
16082                         cls : 'bullet bullet-' + i
16083                     });
16084                 }
16085                 */
16086                 bullets.cn.push({
16087                     cls : 'clear'
16088                 });
16089                 
16090                 cfg.cn[0].cn = bullets;
16091             }
16092         }
16093         
16094         return cfg;
16095     },
16096     
16097     initEvents:  function()
16098     {
16099         if(Roo.isTouch && this.slideOnTouch){
16100             this.el.on("touchstart", this.onTouchStart, this);
16101         }
16102         
16103         if(this.autoslide){
16104             var _this = this;
16105             
16106             this.slideFn = window.setInterval(function() {
16107                 _this.showPanelNext();
16108             }, this.timer);
16109         }
16110         
16111     },
16112     
16113     onTouchStart : function(e, el, o)
16114     {
16115         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16116             return;
16117         }
16118         
16119         this.showPanelNext();
16120     },
16121     
16122     getChildContainer : function()
16123     {
16124         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16125     },
16126     
16127     /**
16128     * register a Navigation item
16129     * @param {Roo.bootstrap.NavItem} the navitem to add
16130     */
16131     register : function(item)
16132     {
16133         this.tabs.push( item);
16134         item.navId = this.navId; // not really needed..
16135         this.addBullet();
16136     
16137     },
16138     
16139     getActivePanel : function()
16140     {
16141         var r = false;
16142         Roo.each(this.tabs, function(t) {
16143             if (t.active) {
16144                 r = t;
16145                 return false;
16146             }
16147             return null;
16148         });
16149         return r;
16150         
16151     },
16152     getPanelByName : function(n)
16153     {
16154         var r = false;
16155         Roo.each(this.tabs, function(t) {
16156             if (t.tabId == n) {
16157                 r = t;
16158                 return false;
16159             }
16160             return null;
16161         });
16162         return r;
16163     },
16164     indexOfPanel : function(p)
16165     {
16166         var r = false;
16167         Roo.each(this.tabs, function(t,i) {
16168             if (t.tabId == p.tabId) {
16169                 r = i;
16170                 return false;
16171             }
16172             return null;
16173         });
16174         return r;
16175     },
16176     /**
16177      * show a specific panel
16178      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16179      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16180      */
16181     showPanel : function (pan)
16182     {
16183         if(this.transition || typeof(pan) == 'undefined'){
16184             Roo.log("waiting for the transitionend");
16185             return;
16186         }
16187         
16188         if (typeof(pan) == 'number') {
16189             pan = this.tabs[pan];
16190         }
16191         
16192         if (typeof(pan) == 'string') {
16193             pan = this.getPanelByName(pan);
16194         }
16195         
16196         var cur = this.getActivePanel();
16197         
16198         if(!pan || !cur){
16199             Roo.log('pan or acitve pan is undefined');
16200             return false;
16201         }
16202         
16203         if (pan.tabId == this.getActivePanel().tabId) {
16204             return true;
16205         }
16206         
16207         if (false === cur.fireEvent('beforedeactivate')) {
16208             return false;
16209         }
16210         
16211         if(this.bullets > 0 && !Roo.isTouch){
16212             this.setActiveBullet(this.indexOfPanel(pan));
16213         }
16214         
16215         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16216             
16217             this.transition = true;
16218             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16219             var lr = dir == 'next' ? 'left' : 'right';
16220             pan.el.addClass(dir); // or prev
16221             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16222             cur.el.addClass(lr); // or right
16223             pan.el.addClass(lr);
16224             
16225             var _this = this;
16226             cur.el.on('transitionend', function() {
16227                 Roo.log("trans end?");
16228                 
16229                 pan.el.removeClass([lr,dir]);
16230                 pan.setActive(true);
16231                 
16232                 cur.el.removeClass([lr]);
16233                 cur.setActive(false);
16234                 
16235                 _this.transition = false;
16236                 
16237             }, this, { single:  true } );
16238             
16239             return true;
16240         }
16241         
16242         cur.setActive(false);
16243         pan.setActive(true);
16244         
16245         return true;
16246         
16247     },
16248     showPanelNext : function()
16249     {
16250         var i = this.indexOfPanel(this.getActivePanel());
16251         
16252         if (i >= this.tabs.length - 1 && !this.autoslide) {
16253             return;
16254         }
16255         
16256         if (i >= this.tabs.length - 1 && this.autoslide) {
16257             i = -1;
16258         }
16259         
16260         this.showPanel(this.tabs[i+1]);
16261     },
16262     
16263     showPanelPrev : function()
16264     {
16265         var i = this.indexOfPanel(this.getActivePanel());
16266         
16267         if (i  < 1 && !this.autoslide) {
16268             return;
16269         }
16270         
16271         if (i < 1 && this.autoslide) {
16272             i = this.tabs.length;
16273         }
16274         
16275         this.showPanel(this.tabs[i-1]);
16276     },
16277     
16278     
16279     addBullet: function()
16280     {
16281         if(!this.bullets || Roo.isTouch){
16282             return;
16283         }
16284         var ctr = this.el.select('.carousel-bullets',true).first();
16285         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16286         var bullet = ctr.createChild({
16287             cls : 'bullet bullet-' + i
16288         },ctr.dom.lastChild);
16289         
16290         
16291         var _this = this;
16292         
16293         bullet.on('click', (function(e, el, o, ii, t){
16294
16295             e.preventDefault();
16296
16297             this.showPanel(ii);
16298
16299             if(this.autoslide && this.slideFn){
16300                 clearInterval(this.slideFn);
16301                 this.slideFn = window.setInterval(function() {
16302                     _this.showPanelNext();
16303                 }, this.timer);
16304             }
16305
16306         }).createDelegate(this, [i, bullet], true));
16307                 
16308         
16309     },
16310      
16311     setActiveBullet : function(i)
16312     {
16313         if(Roo.isTouch){
16314             return;
16315         }
16316         
16317         Roo.each(this.el.select('.bullet', true).elements, function(el){
16318             el.removeClass('selected');
16319         });
16320
16321         var bullet = this.el.select('.bullet-' + i, true).first();
16322         
16323         if(!bullet){
16324             return;
16325         }
16326         
16327         bullet.addClass('selected');
16328     }
16329     
16330     
16331   
16332 });
16333
16334  
16335
16336  
16337  
16338 Roo.apply(Roo.bootstrap.TabGroup, {
16339     
16340     groups: {},
16341      /**
16342     * register a Navigation Group
16343     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16344     */
16345     register : function(navgrp)
16346     {
16347         this.groups[navgrp.navId] = navgrp;
16348         
16349     },
16350     /**
16351     * fetch a Navigation Group based on the navigation ID
16352     * if one does not exist , it will get created.
16353     * @param {string} the navgroup to add
16354     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16355     */
16356     get: function(navId) {
16357         if (typeof(this.groups[navId]) == 'undefined') {
16358             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16359         }
16360         return this.groups[navId] ;
16361     }
16362     
16363     
16364     
16365 });
16366
16367  /*
16368  * - LGPL
16369  *
16370  * TabPanel
16371  * 
16372  */
16373
16374 /**
16375  * @class Roo.bootstrap.TabPanel
16376  * @extends Roo.bootstrap.Component
16377  * Bootstrap TabPanel class
16378  * @cfg {Boolean} active panel active
16379  * @cfg {String} html panel content
16380  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16381  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16382  * 
16383  * 
16384  * @constructor
16385  * Create a new TabPanel
16386  * @param {Object} config The config object
16387  */
16388
16389 Roo.bootstrap.TabPanel = function(config){
16390     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16391     this.addEvents({
16392         /**
16393              * @event changed
16394              * Fires when the active status changes
16395              * @param {Roo.bootstrap.TabPanel} this
16396              * @param {Boolean} state the new state
16397             
16398          */
16399         'changed': true,
16400         /**
16401              * @event beforedeactivate
16402              * Fires before a tab is de-activated - can be used to do validation on a form.
16403              * @param {Roo.bootstrap.TabPanel} this
16404              * @return {Boolean} false if there is an error
16405             
16406          */
16407         'beforedeactivate': true
16408      });
16409     
16410     this.tabId = this.tabId || Roo.id();
16411   
16412 };
16413
16414 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16415     
16416     active: false,
16417     html: false,
16418     tabId: false,
16419     navId : false,
16420     
16421     getAutoCreate : function(){
16422         var cfg = {
16423             tag: 'div',
16424             // item is needed for carousel - not sure if it has any effect otherwise
16425             cls: 'tab-pane item',
16426             html: this.html || ''
16427         };
16428         
16429         if(this.active){
16430             cfg.cls += ' active';
16431         }
16432         
16433         if(this.tabId){
16434             cfg.tabId = this.tabId;
16435         }
16436         
16437         
16438         return cfg;
16439     },
16440     
16441     initEvents:  function()
16442     {
16443         var p = this.parent();
16444         this.navId = this.navId || p.navId;
16445         
16446         if (typeof(this.navId) != 'undefined') {
16447             // not really needed.. but just in case.. parent should be a NavGroup.
16448             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16449             
16450             tg.register(this);
16451             
16452             var i = tg.tabs.length - 1;
16453             
16454             if(this.active && tg.bullets > 0 && i < tg.bullets){
16455                 tg.setActiveBullet(i);
16456             }
16457         }
16458         
16459     },
16460     
16461     
16462     onRender : function(ct, position)
16463     {
16464        // Roo.log("Call onRender: " + this.xtype);
16465         
16466         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16467         
16468         
16469         
16470         
16471         
16472     },
16473     
16474     setActive: function(state)
16475     {
16476         Roo.log("panel - set active " + this.tabId + "=" + state);
16477         
16478         this.active = state;
16479         if (!state) {
16480             this.el.removeClass('active');
16481             
16482         } else  if (!this.el.hasClass('active')) {
16483             this.el.addClass('active');
16484         }
16485         
16486         this.fireEvent('changed', this, state);
16487     }
16488     
16489     
16490 });
16491  
16492
16493  
16494
16495  /*
16496  * - LGPL
16497  *
16498  * DateField
16499  * 
16500  */
16501
16502 /**
16503  * @class Roo.bootstrap.DateField
16504  * @extends Roo.bootstrap.Input
16505  * Bootstrap DateField class
16506  * @cfg {Number} weekStart default 0
16507  * @cfg {String} viewMode default empty, (months|years)
16508  * @cfg {String} minViewMode default empty, (months|years)
16509  * @cfg {Number} startDate default -Infinity
16510  * @cfg {Number} endDate default Infinity
16511  * @cfg {Boolean} todayHighlight default false
16512  * @cfg {Boolean} todayBtn default false
16513  * @cfg {Boolean} calendarWeeks default false
16514  * @cfg {Object} daysOfWeekDisabled default empty
16515  * @cfg {Boolean} singleMode default false (true | false)
16516  * 
16517  * @cfg {Boolean} keyboardNavigation default true
16518  * @cfg {String} language default en
16519  * 
16520  * @constructor
16521  * Create a new DateField
16522  * @param {Object} config The config object
16523  */
16524
16525 Roo.bootstrap.DateField = function(config){
16526     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16527      this.addEvents({
16528             /**
16529              * @event show
16530              * Fires when this field show.
16531              * @param {Roo.bootstrap.DateField} this
16532              * @param {Mixed} date The date value
16533              */
16534             show : true,
16535             /**
16536              * @event show
16537              * Fires when this field hide.
16538              * @param {Roo.bootstrap.DateField} this
16539              * @param {Mixed} date The date value
16540              */
16541             hide : true,
16542             /**
16543              * @event select
16544              * Fires when select a date.
16545              * @param {Roo.bootstrap.DateField} this
16546              * @param {Mixed} date The date value
16547              */
16548             select : true,
16549             /**
16550              * @event beforeselect
16551              * Fires when before select a date.
16552              * @param {Roo.bootstrap.DateField} this
16553              * @param {Mixed} date The date value
16554              */
16555             beforeselect : true
16556         });
16557 };
16558
16559 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16560     
16561     /**
16562      * @cfg {String} format
16563      * The default date format string which can be overriden for localization support.  The format must be
16564      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16565      */
16566     format : "m/d/y",
16567     /**
16568      * @cfg {String} altFormats
16569      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16570      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16571      */
16572     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16573     
16574     weekStart : 0,
16575     
16576     viewMode : '',
16577     
16578     minViewMode : '',
16579     
16580     todayHighlight : false,
16581     
16582     todayBtn: false,
16583     
16584     language: 'en',
16585     
16586     keyboardNavigation: true,
16587     
16588     calendarWeeks: false,
16589     
16590     startDate: -Infinity,
16591     
16592     endDate: Infinity,
16593     
16594     daysOfWeekDisabled: [],
16595     
16596     _events: [],
16597     
16598     singleMode : false,
16599     
16600     UTCDate: function()
16601     {
16602         return new Date(Date.UTC.apply(Date, arguments));
16603     },
16604     
16605     UTCToday: function()
16606     {
16607         var today = new Date();
16608         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16609     },
16610     
16611     getDate: function() {
16612             var d = this.getUTCDate();
16613             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16614     },
16615     
16616     getUTCDate: function() {
16617             return this.date;
16618     },
16619     
16620     setDate: function(d) {
16621             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16622     },
16623     
16624     setUTCDate: function(d) {
16625             this.date = d;
16626             this.setValue(this.formatDate(this.date));
16627     },
16628         
16629     onRender: function(ct, position)
16630     {
16631         
16632         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16633         
16634         this.language = this.language || 'en';
16635         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16636         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16637         
16638         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16639         this.format = this.format || 'm/d/y';
16640         this.isInline = false;
16641         this.isInput = true;
16642         this.component = this.el.select('.add-on', true).first() || false;
16643         this.component = (this.component && this.component.length === 0) ? false : this.component;
16644         this.hasInput = this.component && this.inputEL().length;
16645         
16646         if (typeof(this.minViewMode === 'string')) {
16647             switch (this.minViewMode) {
16648                 case 'months':
16649                     this.minViewMode = 1;
16650                     break;
16651                 case 'years':
16652                     this.minViewMode = 2;
16653                     break;
16654                 default:
16655                     this.minViewMode = 0;
16656                     break;
16657             }
16658         }
16659         
16660         if (typeof(this.viewMode === 'string')) {
16661             switch (this.viewMode) {
16662                 case 'months':
16663                     this.viewMode = 1;
16664                     break;
16665                 case 'years':
16666                     this.viewMode = 2;
16667                     break;
16668                 default:
16669                     this.viewMode = 0;
16670                     break;
16671             }
16672         }
16673                 
16674         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16675         
16676 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16677         
16678         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16679         
16680         this.picker().on('mousedown', this.onMousedown, this);
16681         this.picker().on('click', this.onClick, this);
16682         
16683         this.picker().addClass('datepicker-dropdown');
16684         
16685         this.startViewMode = this.viewMode;
16686         
16687         if(this.singleMode){
16688             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16689                 v.setVisibilityMode(Roo.Element.DISPLAY);
16690                 v.hide();
16691             });
16692             
16693             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16694                 v.setStyle('width', '189px');
16695             });
16696         }
16697         
16698         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16699             if(!this.calendarWeeks){
16700                 v.remove();
16701                 return;
16702             }
16703             
16704             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16705             v.attr('colspan', function(i, val){
16706                 return parseInt(val) + 1;
16707             });
16708         });
16709                         
16710         
16711         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16712         
16713         this.setStartDate(this.startDate);
16714         this.setEndDate(this.endDate);
16715         
16716         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16717         
16718         this.fillDow();
16719         this.fillMonths();
16720         this.update();
16721         this.showMode();
16722         
16723         if(this.isInline) {
16724             this.show();
16725         }
16726     },
16727     
16728     picker : function()
16729     {
16730         return this.pickerEl;
16731 //        return this.el.select('.datepicker', true).first();
16732     },
16733     
16734     fillDow: function()
16735     {
16736         var dowCnt = this.weekStart;
16737         
16738         var dow = {
16739             tag: 'tr',
16740             cn: [
16741                 
16742             ]
16743         };
16744         
16745         if(this.calendarWeeks){
16746             dow.cn.push({
16747                 tag: 'th',
16748                 cls: 'cw',
16749                 html: '&nbsp;'
16750             })
16751         }
16752         
16753         while (dowCnt < this.weekStart + 7) {
16754             dow.cn.push({
16755                 tag: 'th',
16756                 cls: 'dow',
16757                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16758             });
16759         }
16760         
16761         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16762     },
16763     
16764     fillMonths: function()
16765     {    
16766         var i = 0;
16767         var months = this.picker().select('>.datepicker-months td', true).first();
16768         
16769         months.dom.innerHTML = '';
16770         
16771         while (i < 12) {
16772             var month = {
16773                 tag: 'span',
16774                 cls: 'month',
16775                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16776             };
16777             
16778             months.createChild(month);
16779         }
16780         
16781     },
16782     
16783     update: function()
16784     {
16785         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;
16786         
16787         if (this.date < this.startDate) {
16788             this.viewDate = new Date(this.startDate);
16789         } else if (this.date > this.endDate) {
16790             this.viewDate = new Date(this.endDate);
16791         } else {
16792             this.viewDate = new Date(this.date);
16793         }
16794         
16795         this.fill();
16796     },
16797     
16798     fill: function() 
16799     {
16800         var d = new Date(this.viewDate),
16801                 year = d.getUTCFullYear(),
16802                 month = d.getUTCMonth(),
16803                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16804                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16805                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16806                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16807                 currentDate = this.date && this.date.valueOf(),
16808                 today = this.UTCToday();
16809         
16810         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16811         
16812 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16813         
16814 //        this.picker.select('>tfoot th.today').
16815 //                                              .text(dates[this.language].today)
16816 //                                              .toggle(this.todayBtn !== false);
16817     
16818         this.updateNavArrows();
16819         this.fillMonths();
16820                                                 
16821         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16822         
16823         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16824          
16825         prevMonth.setUTCDate(day);
16826         
16827         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16828         
16829         var nextMonth = new Date(prevMonth);
16830         
16831         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16832         
16833         nextMonth = nextMonth.valueOf();
16834         
16835         var fillMonths = false;
16836         
16837         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16838         
16839         while(prevMonth.valueOf() < nextMonth) {
16840             var clsName = '';
16841             
16842             if (prevMonth.getUTCDay() === this.weekStart) {
16843                 if(fillMonths){
16844                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16845                 }
16846                     
16847                 fillMonths = {
16848                     tag: 'tr',
16849                     cn: []
16850                 };
16851                 
16852                 if(this.calendarWeeks){
16853                     // ISO 8601: First week contains first thursday.
16854                     // ISO also states week starts on Monday, but we can be more abstract here.
16855                     var
16856                     // Start of current week: based on weekstart/current date
16857                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16858                     // Thursday of this week
16859                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16860                     // First Thursday of year, year from thursday
16861                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16862                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16863                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16864                     
16865                     fillMonths.cn.push({
16866                         tag: 'td',
16867                         cls: 'cw',
16868                         html: calWeek
16869                     });
16870                 }
16871             }
16872             
16873             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16874                 clsName += ' old';
16875             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16876                 clsName += ' new';
16877             }
16878             if (this.todayHighlight &&
16879                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16880                 prevMonth.getUTCMonth() == today.getMonth() &&
16881                 prevMonth.getUTCDate() == today.getDate()) {
16882                 clsName += ' today';
16883             }
16884             
16885             if (currentDate && prevMonth.valueOf() === currentDate) {
16886                 clsName += ' active';
16887             }
16888             
16889             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16890                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16891                     clsName += ' disabled';
16892             }
16893             
16894             fillMonths.cn.push({
16895                 tag: 'td',
16896                 cls: 'day ' + clsName,
16897                 html: prevMonth.getDate()
16898             });
16899             
16900             prevMonth.setDate(prevMonth.getDate()+1);
16901         }
16902           
16903         var currentYear = this.date && this.date.getUTCFullYear();
16904         var currentMonth = this.date && this.date.getUTCMonth();
16905         
16906         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16907         
16908         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16909             v.removeClass('active');
16910             
16911             if(currentYear === year && k === currentMonth){
16912                 v.addClass('active');
16913             }
16914             
16915             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16916                 v.addClass('disabled');
16917             }
16918             
16919         });
16920         
16921         
16922         year = parseInt(year/10, 10) * 10;
16923         
16924         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16925         
16926         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16927         
16928         year -= 1;
16929         for (var i = -1; i < 11; i++) {
16930             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16931                 tag: 'span',
16932                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16933                 html: year
16934             });
16935             
16936             year += 1;
16937         }
16938     },
16939     
16940     showMode: function(dir) 
16941     {
16942         if (dir) {
16943             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16944         }
16945         
16946         Roo.each(this.picker().select('>div',true).elements, function(v){
16947             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16948             v.hide();
16949         });
16950         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16951     },
16952     
16953     place: function()
16954     {
16955         if(this.isInline) {
16956             return;
16957         }
16958         
16959         this.picker().removeClass(['bottom', 'top']);
16960         
16961         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16962             /*
16963              * place to the top of element!
16964              *
16965              */
16966             
16967             this.picker().addClass('top');
16968             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16969             
16970             return;
16971         }
16972         
16973         this.picker().addClass('bottom');
16974         
16975         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16976     },
16977     
16978     parseDate : function(value)
16979     {
16980         if(!value || value instanceof Date){
16981             return value;
16982         }
16983         var v = Date.parseDate(value, this.format);
16984         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16985             v = Date.parseDate(value, 'Y-m-d');
16986         }
16987         if(!v && this.altFormats){
16988             if(!this.altFormatsArray){
16989                 this.altFormatsArray = this.altFormats.split("|");
16990             }
16991             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16992                 v = Date.parseDate(value, this.altFormatsArray[i]);
16993             }
16994         }
16995         return v;
16996     },
16997     
16998     formatDate : function(date, fmt)
16999     {   
17000         return (!date || !(date instanceof Date)) ?
17001         date : date.dateFormat(fmt || this.format);
17002     },
17003     
17004     onFocus : function()
17005     {
17006         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17007         this.show();
17008     },
17009     
17010     onBlur : function()
17011     {
17012         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17013         
17014         var d = this.inputEl().getValue();
17015         
17016         this.setValue(d);
17017                 
17018         this.hide();
17019     },
17020     
17021     show : function()
17022     {
17023         this.picker().show();
17024         this.update();
17025         this.place();
17026         
17027         this.fireEvent('show', this, this.date);
17028     },
17029     
17030     hide : function()
17031     {
17032         if(this.isInline) {
17033             return;
17034         }
17035         this.picker().hide();
17036         this.viewMode = this.startViewMode;
17037         this.showMode();
17038         
17039         this.fireEvent('hide', this, this.date);
17040         
17041     },
17042     
17043     onMousedown: function(e)
17044     {
17045         e.stopPropagation();
17046         e.preventDefault();
17047     },
17048     
17049     keyup: function(e)
17050     {
17051         Roo.bootstrap.DateField.superclass.keyup.call(this);
17052         this.update();
17053     },
17054
17055     setValue: function(v)
17056     {
17057         if(this.fireEvent('beforeselect', this, v) !== false){
17058             var d = new Date(this.parseDate(v) ).clearTime();
17059         
17060             if(isNaN(d.getTime())){
17061                 this.date = this.viewDate = '';
17062                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17063                 return;
17064             }
17065
17066             v = this.formatDate(d);
17067
17068             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17069
17070             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17071
17072             this.update();
17073
17074             this.fireEvent('select', this, this.date);
17075         }
17076     },
17077     
17078     getValue: function()
17079     {
17080         return this.formatDate(this.date);
17081     },
17082     
17083     fireKey: function(e)
17084     {
17085         if (!this.picker().isVisible()){
17086             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17087                 this.show();
17088             }
17089             return;
17090         }
17091         
17092         var dateChanged = false,
17093         dir, day, month,
17094         newDate, newViewDate;
17095         
17096         switch(e.keyCode){
17097             case 27: // escape
17098                 this.hide();
17099                 e.preventDefault();
17100                 break;
17101             case 37: // left
17102             case 39: // right
17103                 if (!this.keyboardNavigation) {
17104                     break;
17105                 }
17106                 dir = e.keyCode == 37 ? -1 : 1;
17107                 
17108                 if (e.ctrlKey){
17109                     newDate = this.moveYear(this.date, dir);
17110                     newViewDate = this.moveYear(this.viewDate, dir);
17111                 } else if (e.shiftKey){
17112                     newDate = this.moveMonth(this.date, dir);
17113                     newViewDate = this.moveMonth(this.viewDate, dir);
17114                 } else {
17115                     newDate = new Date(this.date);
17116                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17117                     newViewDate = new Date(this.viewDate);
17118                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17119                 }
17120                 if (this.dateWithinRange(newDate)){
17121                     this.date = newDate;
17122                     this.viewDate = newViewDate;
17123                     this.setValue(this.formatDate(this.date));
17124 //                    this.update();
17125                     e.preventDefault();
17126                     dateChanged = true;
17127                 }
17128                 break;
17129             case 38: // up
17130             case 40: // down
17131                 if (!this.keyboardNavigation) {
17132                     break;
17133                 }
17134                 dir = e.keyCode == 38 ? -1 : 1;
17135                 if (e.ctrlKey){
17136                     newDate = this.moveYear(this.date, dir);
17137                     newViewDate = this.moveYear(this.viewDate, dir);
17138                 } else if (e.shiftKey){
17139                     newDate = this.moveMonth(this.date, dir);
17140                     newViewDate = this.moveMonth(this.viewDate, dir);
17141                 } else {
17142                     newDate = new Date(this.date);
17143                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17144                     newViewDate = new Date(this.viewDate);
17145                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17146                 }
17147                 if (this.dateWithinRange(newDate)){
17148                     this.date = newDate;
17149                     this.viewDate = newViewDate;
17150                     this.setValue(this.formatDate(this.date));
17151 //                    this.update();
17152                     e.preventDefault();
17153                     dateChanged = true;
17154                 }
17155                 break;
17156             case 13: // enter
17157                 this.setValue(this.formatDate(this.date));
17158                 this.hide();
17159                 e.preventDefault();
17160                 break;
17161             case 9: // tab
17162                 this.setValue(this.formatDate(this.date));
17163                 this.hide();
17164                 break;
17165             case 16: // shift
17166             case 17: // ctrl
17167             case 18: // alt
17168                 break;
17169             default :
17170                 this.hide();
17171                 
17172         }
17173     },
17174     
17175     
17176     onClick: function(e) 
17177     {
17178         e.stopPropagation();
17179         e.preventDefault();
17180         
17181         var target = e.getTarget();
17182         
17183         if(target.nodeName.toLowerCase() === 'i'){
17184             target = Roo.get(target).dom.parentNode;
17185         }
17186         
17187         var nodeName = target.nodeName;
17188         var className = target.className;
17189         var html = target.innerHTML;
17190         //Roo.log(nodeName);
17191         
17192         switch(nodeName.toLowerCase()) {
17193             case 'th':
17194                 switch(className) {
17195                     case 'switch':
17196                         this.showMode(1);
17197                         break;
17198                     case 'prev':
17199                     case 'next':
17200                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17201                         switch(this.viewMode){
17202                                 case 0:
17203                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17204                                         break;
17205                                 case 1:
17206                                 case 2:
17207                                         this.viewDate = this.moveYear(this.viewDate, dir);
17208                                         break;
17209                         }
17210                         this.fill();
17211                         break;
17212                     case 'today':
17213                         var date = new Date();
17214                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17215 //                        this.fill()
17216                         this.setValue(this.formatDate(this.date));
17217                         
17218                         this.hide();
17219                         break;
17220                 }
17221                 break;
17222             case 'span':
17223                 if (className.indexOf('disabled') < 0) {
17224                     this.viewDate.setUTCDate(1);
17225                     if (className.indexOf('month') > -1) {
17226                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17227                     } else {
17228                         var year = parseInt(html, 10) || 0;
17229                         this.viewDate.setUTCFullYear(year);
17230                         
17231                     }
17232                     
17233                     if(this.singleMode){
17234                         this.setValue(this.formatDate(this.viewDate));
17235                         this.hide();
17236                         return;
17237                     }
17238                     
17239                     this.showMode(-1);
17240                     this.fill();
17241                 }
17242                 break;
17243                 
17244             case 'td':
17245                 //Roo.log(className);
17246                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17247                     var day = parseInt(html, 10) || 1;
17248                     var year = this.viewDate.getUTCFullYear(),
17249                         month = this.viewDate.getUTCMonth();
17250
17251                     if (className.indexOf('old') > -1) {
17252                         if(month === 0 ){
17253                             month = 11;
17254                             year -= 1;
17255                         }else{
17256                             month -= 1;
17257                         }
17258                     } else if (className.indexOf('new') > -1) {
17259                         if (month == 11) {
17260                             month = 0;
17261                             year += 1;
17262                         } else {
17263                             month += 1;
17264                         }
17265                     }
17266                     //Roo.log([year,month,day]);
17267                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17268                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17269 //                    this.fill();
17270                     //Roo.log(this.formatDate(this.date));
17271                     this.setValue(this.formatDate(this.date));
17272                     this.hide();
17273                 }
17274                 break;
17275         }
17276     },
17277     
17278     setStartDate: function(startDate)
17279     {
17280         this.startDate = startDate || -Infinity;
17281         if (this.startDate !== -Infinity) {
17282             this.startDate = this.parseDate(this.startDate);
17283         }
17284         this.update();
17285         this.updateNavArrows();
17286     },
17287
17288     setEndDate: function(endDate)
17289     {
17290         this.endDate = endDate || Infinity;
17291         if (this.endDate !== Infinity) {
17292             this.endDate = this.parseDate(this.endDate);
17293         }
17294         this.update();
17295         this.updateNavArrows();
17296     },
17297     
17298     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17299     {
17300         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17301         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17302             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17303         }
17304         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17305             return parseInt(d, 10);
17306         });
17307         this.update();
17308         this.updateNavArrows();
17309     },
17310     
17311     updateNavArrows: function() 
17312     {
17313         if(this.singleMode){
17314             return;
17315         }
17316         
17317         var d = new Date(this.viewDate),
17318         year = d.getUTCFullYear(),
17319         month = d.getUTCMonth();
17320         
17321         Roo.each(this.picker().select('.prev', true).elements, function(v){
17322             v.show();
17323             switch (this.viewMode) {
17324                 case 0:
17325
17326                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17327                         v.hide();
17328                     }
17329                     break;
17330                 case 1:
17331                 case 2:
17332                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17333                         v.hide();
17334                     }
17335                     break;
17336             }
17337         });
17338         
17339         Roo.each(this.picker().select('.next', true).elements, function(v){
17340             v.show();
17341             switch (this.viewMode) {
17342                 case 0:
17343
17344                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17345                         v.hide();
17346                     }
17347                     break;
17348                 case 1:
17349                 case 2:
17350                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17351                         v.hide();
17352                     }
17353                     break;
17354             }
17355         })
17356     },
17357     
17358     moveMonth: function(date, dir)
17359     {
17360         if (!dir) {
17361             return date;
17362         }
17363         var new_date = new Date(date.valueOf()),
17364         day = new_date.getUTCDate(),
17365         month = new_date.getUTCMonth(),
17366         mag = Math.abs(dir),
17367         new_month, test;
17368         dir = dir > 0 ? 1 : -1;
17369         if (mag == 1){
17370             test = dir == -1
17371             // If going back one month, make sure month is not current month
17372             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17373             ? function(){
17374                 return new_date.getUTCMonth() == month;
17375             }
17376             // If going forward one month, make sure month is as expected
17377             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17378             : function(){
17379                 return new_date.getUTCMonth() != new_month;
17380             };
17381             new_month = month + dir;
17382             new_date.setUTCMonth(new_month);
17383             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17384             if (new_month < 0 || new_month > 11) {
17385                 new_month = (new_month + 12) % 12;
17386             }
17387         } else {
17388             // For magnitudes >1, move one month at a time...
17389             for (var i=0; i<mag; i++) {
17390                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17391                 new_date = this.moveMonth(new_date, dir);
17392             }
17393             // ...then reset the day, keeping it in the new month
17394             new_month = new_date.getUTCMonth();
17395             new_date.setUTCDate(day);
17396             test = function(){
17397                 return new_month != new_date.getUTCMonth();
17398             };
17399         }
17400         // Common date-resetting loop -- if date is beyond end of month, make it
17401         // end of month
17402         while (test()){
17403             new_date.setUTCDate(--day);
17404             new_date.setUTCMonth(new_month);
17405         }
17406         return new_date;
17407     },
17408
17409     moveYear: function(date, dir)
17410     {
17411         return this.moveMonth(date, dir*12);
17412     },
17413
17414     dateWithinRange: function(date)
17415     {
17416         return date >= this.startDate && date <= this.endDate;
17417     },
17418
17419     
17420     remove: function() 
17421     {
17422         this.picker().remove();
17423     }
17424    
17425 });
17426
17427 Roo.apply(Roo.bootstrap.DateField,  {
17428     
17429     head : {
17430         tag: 'thead',
17431         cn: [
17432         {
17433             tag: 'tr',
17434             cn: [
17435             {
17436                 tag: 'th',
17437                 cls: 'prev',
17438                 html: '<i class="fa fa-arrow-left"/>'
17439             },
17440             {
17441                 tag: 'th',
17442                 cls: 'switch',
17443                 colspan: '5'
17444             },
17445             {
17446                 tag: 'th',
17447                 cls: 'next',
17448                 html: '<i class="fa fa-arrow-right"/>'
17449             }
17450
17451             ]
17452         }
17453         ]
17454     },
17455     
17456     content : {
17457         tag: 'tbody',
17458         cn: [
17459         {
17460             tag: 'tr',
17461             cn: [
17462             {
17463                 tag: 'td',
17464                 colspan: '7'
17465             }
17466             ]
17467         }
17468         ]
17469     },
17470     
17471     footer : {
17472         tag: 'tfoot',
17473         cn: [
17474         {
17475             tag: 'tr',
17476             cn: [
17477             {
17478                 tag: 'th',
17479                 colspan: '7',
17480                 cls: 'today'
17481             }
17482                     
17483             ]
17484         }
17485         ]
17486     },
17487     
17488     dates:{
17489         en: {
17490             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17491             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17492             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17493             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17494             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17495             today: "Today"
17496         }
17497     },
17498     
17499     modes: [
17500     {
17501         clsName: 'days',
17502         navFnc: 'Month',
17503         navStep: 1
17504     },
17505     {
17506         clsName: 'months',
17507         navFnc: 'FullYear',
17508         navStep: 1
17509     },
17510     {
17511         clsName: 'years',
17512         navFnc: 'FullYear',
17513         navStep: 10
17514     }]
17515 });
17516
17517 Roo.apply(Roo.bootstrap.DateField,  {
17518   
17519     template : {
17520         tag: 'div',
17521         cls: 'datepicker dropdown-menu roo-dynamic',
17522         cn: [
17523         {
17524             tag: 'div',
17525             cls: 'datepicker-days',
17526             cn: [
17527             {
17528                 tag: 'table',
17529                 cls: 'table-condensed',
17530                 cn:[
17531                 Roo.bootstrap.DateField.head,
17532                 {
17533                     tag: 'tbody'
17534                 },
17535                 Roo.bootstrap.DateField.footer
17536                 ]
17537             }
17538             ]
17539         },
17540         {
17541             tag: 'div',
17542             cls: 'datepicker-months',
17543             cn: [
17544             {
17545                 tag: 'table',
17546                 cls: 'table-condensed',
17547                 cn:[
17548                 Roo.bootstrap.DateField.head,
17549                 Roo.bootstrap.DateField.content,
17550                 Roo.bootstrap.DateField.footer
17551                 ]
17552             }
17553             ]
17554         },
17555         {
17556             tag: 'div',
17557             cls: 'datepicker-years',
17558             cn: [
17559             {
17560                 tag: 'table',
17561                 cls: 'table-condensed',
17562                 cn:[
17563                 Roo.bootstrap.DateField.head,
17564                 Roo.bootstrap.DateField.content,
17565                 Roo.bootstrap.DateField.footer
17566                 ]
17567             }
17568             ]
17569         }
17570         ]
17571     }
17572 });
17573
17574  
17575
17576  /*
17577  * - LGPL
17578  *
17579  * TimeField
17580  * 
17581  */
17582
17583 /**
17584  * @class Roo.bootstrap.TimeField
17585  * @extends Roo.bootstrap.Input
17586  * Bootstrap DateField class
17587  * 
17588  * 
17589  * @constructor
17590  * Create a new TimeField
17591  * @param {Object} config The config object
17592  */
17593
17594 Roo.bootstrap.TimeField = function(config){
17595     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17596     this.addEvents({
17597             /**
17598              * @event show
17599              * Fires when this field show.
17600              * @param {Roo.bootstrap.DateField} thisthis
17601              * @param {Mixed} date The date value
17602              */
17603             show : true,
17604             /**
17605              * @event show
17606              * Fires when this field hide.
17607              * @param {Roo.bootstrap.DateField} this
17608              * @param {Mixed} date The date value
17609              */
17610             hide : true,
17611             /**
17612              * @event select
17613              * Fires when select a date.
17614              * @param {Roo.bootstrap.DateField} this
17615              * @param {Mixed} date The date value
17616              */
17617             select : true
17618         });
17619 };
17620
17621 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17622     
17623     /**
17624      * @cfg {String} format
17625      * The default time format string which can be overriden for localization support.  The format must be
17626      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17627      */
17628     format : "H:i",
17629        
17630     onRender: function(ct, position)
17631     {
17632         
17633         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17634                 
17635         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17636         
17637         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17638         
17639         this.pop = this.picker().select('>.datepicker-time',true).first();
17640         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17641         
17642         this.picker().on('mousedown', this.onMousedown, this);
17643         this.picker().on('click', this.onClick, this);
17644         
17645         this.picker().addClass('datepicker-dropdown');
17646     
17647         this.fillTime();
17648         this.update();
17649             
17650         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17651         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17652         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17653         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17654         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17655         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17656
17657     },
17658     
17659     fireKey: function(e){
17660         if (!this.picker().isVisible()){
17661             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17662                 this.show();
17663             }
17664             return;
17665         }
17666
17667         e.preventDefault();
17668         
17669         switch(e.keyCode){
17670             case 27: // escape
17671                 this.hide();
17672                 break;
17673             case 37: // left
17674             case 39: // right
17675                 this.onTogglePeriod();
17676                 break;
17677             case 38: // up
17678                 this.onIncrementMinutes();
17679                 break;
17680             case 40: // down
17681                 this.onDecrementMinutes();
17682                 break;
17683             case 13: // enter
17684             case 9: // tab
17685                 this.setTime();
17686                 break;
17687         }
17688     },
17689     
17690     onClick: function(e) {
17691         e.stopPropagation();
17692         e.preventDefault();
17693     },
17694     
17695     picker : function()
17696     {
17697         return this.el.select('.datepicker', true).first();
17698     },
17699     
17700     fillTime: function()
17701     {    
17702         var time = this.pop.select('tbody', true).first();
17703         
17704         time.dom.innerHTML = '';
17705         
17706         time.createChild({
17707             tag: 'tr',
17708             cn: [
17709                 {
17710                     tag: 'td',
17711                     cn: [
17712                         {
17713                             tag: 'a',
17714                             href: '#',
17715                             cls: 'btn',
17716                             cn: [
17717                                 {
17718                                     tag: 'span',
17719                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17720                                 }
17721                             ]
17722                         } 
17723                     ]
17724                 },
17725                 {
17726                     tag: 'td',
17727                     cls: 'separator'
17728                 },
17729                 {
17730                     tag: 'td',
17731                     cn: [
17732                         {
17733                             tag: 'a',
17734                             href: '#',
17735                             cls: 'btn',
17736                             cn: [
17737                                 {
17738                                     tag: 'span',
17739                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17740                                 }
17741                             ]
17742                         }
17743                     ]
17744                 },
17745                 {
17746                     tag: 'td',
17747                     cls: 'separator'
17748                 }
17749             ]
17750         });
17751         
17752         time.createChild({
17753             tag: 'tr',
17754             cn: [
17755                 {
17756                     tag: 'td',
17757                     cn: [
17758                         {
17759                             tag: 'span',
17760                             cls: 'timepicker-hour',
17761                             html: '00'
17762                         }  
17763                     ]
17764                 },
17765                 {
17766                     tag: 'td',
17767                     cls: 'separator',
17768                     html: ':'
17769                 },
17770                 {
17771                     tag: 'td',
17772                     cn: [
17773                         {
17774                             tag: 'span',
17775                             cls: 'timepicker-minute',
17776                             html: '00'
17777                         }  
17778                     ]
17779                 },
17780                 {
17781                     tag: 'td',
17782                     cls: 'separator'
17783                 },
17784                 {
17785                     tag: 'td',
17786                     cn: [
17787                         {
17788                             tag: 'button',
17789                             type: 'button',
17790                             cls: 'btn btn-primary period',
17791                             html: 'AM'
17792                             
17793                         }
17794                     ]
17795                 }
17796             ]
17797         });
17798         
17799         time.createChild({
17800             tag: 'tr',
17801             cn: [
17802                 {
17803                     tag: 'td',
17804                     cn: [
17805                         {
17806                             tag: 'a',
17807                             href: '#',
17808                             cls: 'btn',
17809                             cn: [
17810                                 {
17811                                     tag: 'span',
17812                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17813                                 }
17814                             ]
17815                         }
17816                     ]
17817                 },
17818                 {
17819                     tag: 'td',
17820                     cls: 'separator'
17821                 },
17822                 {
17823                     tag: 'td',
17824                     cn: [
17825                         {
17826                             tag: 'a',
17827                             href: '#',
17828                             cls: 'btn',
17829                             cn: [
17830                                 {
17831                                     tag: 'span',
17832                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17833                                 }
17834                             ]
17835                         }
17836                     ]
17837                 },
17838                 {
17839                     tag: 'td',
17840                     cls: 'separator'
17841                 }
17842             ]
17843         });
17844         
17845     },
17846     
17847     update: function()
17848     {
17849         
17850         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17851         
17852         this.fill();
17853     },
17854     
17855     fill: function() 
17856     {
17857         var hours = this.time.getHours();
17858         var minutes = this.time.getMinutes();
17859         var period = 'AM';
17860         
17861         if(hours > 11){
17862             period = 'PM';
17863         }
17864         
17865         if(hours == 0){
17866             hours = 12;
17867         }
17868         
17869         
17870         if(hours > 12){
17871             hours = hours - 12;
17872         }
17873         
17874         if(hours < 10){
17875             hours = '0' + hours;
17876         }
17877         
17878         if(minutes < 10){
17879             minutes = '0' + minutes;
17880         }
17881         
17882         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17883         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17884         this.pop.select('button', true).first().dom.innerHTML = period;
17885         
17886     },
17887     
17888     place: function()
17889     {   
17890         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17891         
17892         var cls = ['bottom'];
17893         
17894         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17895             cls.pop();
17896             cls.push('top');
17897         }
17898         
17899         cls.push('right');
17900         
17901         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17902             cls.pop();
17903             cls.push('left');
17904         }
17905         
17906         this.picker().addClass(cls.join('-'));
17907         
17908         var _this = this;
17909         
17910         Roo.each(cls, function(c){
17911             if(c == 'bottom'){
17912                 _this.picker().setTop(_this.inputEl().getHeight());
17913                 return;
17914             }
17915             if(c == 'top'){
17916                 _this.picker().setTop(0 - _this.picker().getHeight());
17917                 return;
17918             }
17919             
17920             if(c == 'left'){
17921                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17922                 return;
17923             }
17924             if(c == 'right'){
17925                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17926                 return;
17927             }
17928         });
17929         
17930     },
17931   
17932     onFocus : function()
17933     {
17934         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17935         this.show();
17936     },
17937     
17938     onBlur : function()
17939     {
17940         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17941         this.hide();
17942     },
17943     
17944     show : function()
17945     {
17946         this.picker().show();
17947         this.pop.show();
17948         this.update();
17949         this.place();
17950         
17951         this.fireEvent('show', this, this.date);
17952     },
17953     
17954     hide : function()
17955     {
17956         this.picker().hide();
17957         this.pop.hide();
17958         
17959         this.fireEvent('hide', this, this.date);
17960     },
17961     
17962     setTime : function()
17963     {
17964         this.hide();
17965         this.setValue(this.time.format(this.format));
17966         
17967         this.fireEvent('select', this, this.date);
17968         
17969         
17970     },
17971     
17972     onMousedown: function(e){
17973         e.stopPropagation();
17974         e.preventDefault();
17975     },
17976     
17977     onIncrementHours: function()
17978     {
17979         Roo.log('onIncrementHours');
17980         this.time = this.time.add(Date.HOUR, 1);
17981         this.update();
17982         
17983     },
17984     
17985     onDecrementHours: function()
17986     {
17987         Roo.log('onDecrementHours');
17988         this.time = this.time.add(Date.HOUR, -1);
17989         this.update();
17990     },
17991     
17992     onIncrementMinutes: function()
17993     {
17994         Roo.log('onIncrementMinutes');
17995         this.time = this.time.add(Date.MINUTE, 1);
17996         this.update();
17997     },
17998     
17999     onDecrementMinutes: function()
18000     {
18001         Roo.log('onDecrementMinutes');
18002         this.time = this.time.add(Date.MINUTE, -1);
18003         this.update();
18004     },
18005     
18006     onTogglePeriod: function()
18007     {
18008         Roo.log('onTogglePeriod');
18009         this.time = this.time.add(Date.HOUR, 12);
18010         this.update();
18011     }
18012     
18013    
18014 });
18015
18016 Roo.apply(Roo.bootstrap.TimeField,  {
18017     
18018     content : {
18019         tag: 'tbody',
18020         cn: [
18021             {
18022                 tag: 'tr',
18023                 cn: [
18024                 {
18025                     tag: 'td',
18026                     colspan: '7'
18027                 }
18028                 ]
18029             }
18030         ]
18031     },
18032     
18033     footer : {
18034         tag: 'tfoot',
18035         cn: [
18036             {
18037                 tag: 'tr',
18038                 cn: [
18039                 {
18040                     tag: 'th',
18041                     colspan: '7',
18042                     cls: '',
18043                     cn: [
18044                         {
18045                             tag: 'button',
18046                             cls: 'btn btn-info ok',
18047                             html: 'OK'
18048                         }
18049                     ]
18050                 }
18051
18052                 ]
18053             }
18054         ]
18055     }
18056 });
18057
18058 Roo.apply(Roo.bootstrap.TimeField,  {
18059   
18060     template : {
18061         tag: 'div',
18062         cls: 'datepicker dropdown-menu',
18063         cn: [
18064             {
18065                 tag: 'div',
18066                 cls: 'datepicker-time',
18067                 cn: [
18068                 {
18069                     tag: 'table',
18070                     cls: 'table-condensed',
18071                     cn:[
18072                     Roo.bootstrap.TimeField.content,
18073                     Roo.bootstrap.TimeField.footer
18074                     ]
18075                 }
18076                 ]
18077             }
18078         ]
18079     }
18080 });
18081
18082  
18083
18084  /*
18085  * - LGPL
18086  *
18087  * MonthField
18088  * 
18089  */
18090
18091 /**
18092  * @class Roo.bootstrap.MonthField
18093  * @extends Roo.bootstrap.Input
18094  * Bootstrap MonthField class
18095  * 
18096  * @cfg {String} language default en
18097  * 
18098  * @constructor
18099  * Create a new MonthField
18100  * @param {Object} config The config object
18101  */
18102
18103 Roo.bootstrap.MonthField = function(config){
18104     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18105     
18106     this.addEvents({
18107         /**
18108          * @event show
18109          * Fires when this field show.
18110          * @param {Roo.bootstrap.MonthField} this
18111          * @param {Mixed} date The date value
18112          */
18113         show : true,
18114         /**
18115          * @event show
18116          * Fires when this field hide.
18117          * @param {Roo.bootstrap.MonthField} this
18118          * @param {Mixed} date The date value
18119          */
18120         hide : true,
18121         /**
18122          * @event select
18123          * Fires when select a date.
18124          * @param {Roo.bootstrap.MonthField} this
18125          * @param {String} oldvalue The old value
18126          * @param {String} newvalue The new value
18127          */
18128         select : true
18129     });
18130 };
18131
18132 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18133     
18134     onRender: function(ct, position)
18135     {
18136         
18137         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18138         
18139         this.language = this.language || 'en';
18140         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18141         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18142         
18143         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18144         this.isInline = false;
18145         this.isInput = true;
18146         this.component = this.el.select('.add-on', true).first() || false;
18147         this.component = (this.component && this.component.length === 0) ? false : this.component;
18148         this.hasInput = this.component && this.inputEL().length;
18149         
18150         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18151         
18152         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18153         
18154         this.picker().on('mousedown', this.onMousedown, this);
18155         this.picker().on('click', this.onClick, this);
18156         
18157         this.picker().addClass('datepicker-dropdown');
18158         
18159         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18160             v.setStyle('width', '189px');
18161         });
18162         
18163         this.fillMonths();
18164         
18165         this.update();
18166         
18167         if(this.isInline) {
18168             this.show();
18169         }
18170         
18171     },
18172     
18173     setValue: function(v, suppressEvent)
18174     {   
18175         var o = this.getValue();
18176         
18177         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18178         
18179         this.update();
18180
18181         if(suppressEvent !== true){
18182             this.fireEvent('select', this, o, v);
18183         }
18184         
18185     },
18186     
18187     getValue: function()
18188     {
18189         return this.value;
18190     },
18191     
18192     onClick: function(e) 
18193     {
18194         e.stopPropagation();
18195         e.preventDefault();
18196         
18197         var target = e.getTarget();
18198         
18199         if(target.nodeName.toLowerCase() === 'i'){
18200             target = Roo.get(target).dom.parentNode;
18201         }
18202         
18203         var nodeName = target.nodeName;
18204         var className = target.className;
18205         var html = target.innerHTML;
18206         
18207         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18208             return;
18209         }
18210         
18211         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18212         
18213         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18214         
18215         this.hide();
18216                         
18217     },
18218     
18219     picker : function()
18220     {
18221         return this.pickerEl;
18222     },
18223     
18224     fillMonths: function()
18225     {    
18226         var i = 0;
18227         var months = this.picker().select('>.datepicker-months td', true).first();
18228         
18229         months.dom.innerHTML = '';
18230         
18231         while (i < 12) {
18232             var month = {
18233                 tag: 'span',
18234                 cls: 'month',
18235                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18236             };
18237             
18238             months.createChild(month);
18239         }
18240         
18241     },
18242     
18243     update: function()
18244     {
18245         var _this = this;
18246         
18247         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18248             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18249         }
18250         
18251         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18252             e.removeClass('active');
18253             
18254             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18255                 e.addClass('active');
18256             }
18257         })
18258     },
18259     
18260     place: function()
18261     {
18262         if(this.isInline) {
18263             return;
18264         }
18265         
18266         this.picker().removeClass(['bottom', 'top']);
18267         
18268         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18269             /*
18270              * place to the top of element!
18271              *
18272              */
18273             
18274             this.picker().addClass('top');
18275             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18276             
18277             return;
18278         }
18279         
18280         this.picker().addClass('bottom');
18281         
18282         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18283     },
18284     
18285     onFocus : function()
18286     {
18287         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18288         this.show();
18289     },
18290     
18291     onBlur : function()
18292     {
18293         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18294         
18295         var d = this.inputEl().getValue();
18296         
18297         this.setValue(d);
18298                 
18299         this.hide();
18300     },
18301     
18302     show : function()
18303     {
18304         this.picker().show();
18305         this.picker().select('>.datepicker-months', true).first().show();
18306         this.update();
18307         this.place();
18308         
18309         this.fireEvent('show', this, this.date);
18310     },
18311     
18312     hide : function()
18313     {
18314         if(this.isInline) {
18315             return;
18316         }
18317         this.picker().hide();
18318         this.fireEvent('hide', this, this.date);
18319         
18320     },
18321     
18322     onMousedown: function(e)
18323     {
18324         e.stopPropagation();
18325         e.preventDefault();
18326     },
18327     
18328     keyup: function(e)
18329     {
18330         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18331         this.update();
18332     },
18333
18334     fireKey: function(e)
18335     {
18336         if (!this.picker().isVisible()){
18337             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18338                 this.show();
18339             }
18340             return;
18341         }
18342         
18343         var dir;
18344         
18345         switch(e.keyCode){
18346             case 27: // escape
18347                 this.hide();
18348                 e.preventDefault();
18349                 break;
18350             case 37: // left
18351             case 39: // right
18352                 dir = e.keyCode == 37 ? -1 : 1;
18353                 
18354                 this.vIndex = this.vIndex + dir;
18355                 
18356                 if(this.vIndex < 0){
18357                     this.vIndex = 0;
18358                 }
18359                 
18360                 if(this.vIndex > 11){
18361                     this.vIndex = 11;
18362                 }
18363                 
18364                 if(isNaN(this.vIndex)){
18365                     this.vIndex = 0;
18366                 }
18367                 
18368                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18369                 
18370                 break;
18371             case 38: // up
18372             case 40: // down
18373                 
18374                 dir = e.keyCode == 38 ? -1 : 1;
18375                 
18376                 this.vIndex = this.vIndex + dir * 4;
18377                 
18378                 if(this.vIndex < 0){
18379                     this.vIndex = 0;
18380                 }
18381                 
18382                 if(this.vIndex > 11){
18383                     this.vIndex = 11;
18384                 }
18385                 
18386                 if(isNaN(this.vIndex)){
18387                     this.vIndex = 0;
18388                 }
18389                 
18390                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18391                 break;
18392                 
18393             case 13: // enter
18394                 
18395                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18396                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18397                 }
18398                 
18399                 this.hide();
18400                 e.preventDefault();
18401                 break;
18402             case 9: // tab
18403                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18404                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18405                 }
18406                 this.hide();
18407                 break;
18408             case 16: // shift
18409             case 17: // ctrl
18410             case 18: // alt
18411                 break;
18412             default :
18413                 this.hide();
18414                 
18415         }
18416     },
18417     
18418     remove: function() 
18419     {
18420         this.picker().remove();
18421     }
18422    
18423 });
18424
18425 Roo.apply(Roo.bootstrap.MonthField,  {
18426     
18427     content : {
18428         tag: 'tbody',
18429         cn: [
18430         {
18431             tag: 'tr',
18432             cn: [
18433             {
18434                 tag: 'td',
18435                 colspan: '7'
18436             }
18437             ]
18438         }
18439         ]
18440     },
18441     
18442     dates:{
18443         en: {
18444             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18445             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18446         }
18447     }
18448 });
18449
18450 Roo.apply(Roo.bootstrap.MonthField,  {
18451   
18452     template : {
18453         tag: 'div',
18454         cls: 'datepicker dropdown-menu roo-dynamic',
18455         cn: [
18456             {
18457                 tag: 'div',
18458                 cls: 'datepicker-months',
18459                 cn: [
18460                 {
18461                     tag: 'table',
18462                     cls: 'table-condensed',
18463                     cn:[
18464                         Roo.bootstrap.DateField.content
18465                     ]
18466                 }
18467                 ]
18468             }
18469         ]
18470     }
18471 });
18472
18473  
18474
18475  
18476  /*
18477  * - LGPL
18478  *
18479  * CheckBox
18480  * 
18481  */
18482
18483 /**
18484  * @class Roo.bootstrap.CheckBox
18485  * @extends Roo.bootstrap.Input
18486  * Bootstrap CheckBox class
18487  * 
18488  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18489  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18490  * @cfg {String} boxLabel The text that appears beside the checkbox
18491  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18492  * @cfg {Boolean} checked initnal the element
18493  * @cfg {Boolean} inline inline the element (default false)
18494  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18495  * 
18496  * @constructor
18497  * Create a new CheckBox
18498  * @param {Object} config The config object
18499  */
18500
18501 Roo.bootstrap.CheckBox = function(config){
18502     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18503    
18504     this.addEvents({
18505         /**
18506         * @event check
18507         * Fires when the element is checked or unchecked.
18508         * @param {Roo.bootstrap.CheckBox} this This input
18509         * @param {Boolean} checked The new checked value
18510         */
18511        check : true
18512     });
18513     
18514 };
18515
18516 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18517   
18518     inputType: 'checkbox',
18519     inputValue: 1,
18520     valueOff: 0,
18521     boxLabel: false,
18522     checked: false,
18523     weight : false,
18524     inline: false,
18525     
18526     getAutoCreate : function()
18527     {
18528         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18529         
18530         var id = Roo.id();
18531         
18532         var cfg = {};
18533         
18534         cfg.cls = 'form-group ' + this.inputType; //input-group
18535         
18536         if(this.inline){
18537             cfg.cls += ' ' + this.inputType + '-inline';
18538         }
18539         
18540         var input =  {
18541             tag: 'input',
18542             id : id,
18543             type : this.inputType,
18544             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18545             cls : 'roo-' + this.inputType, //'form-box',
18546             placeholder : this.placeholder || ''
18547             
18548         };
18549         
18550         if (this.weight) { // Validity check?
18551             cfg.cls += " " + this.inputType + "-" + this.weight;
18552         }
18553         
18554         if (this.disabled) {
18555             input.disabled=true;
18556         }
18557         
18558         if(this.checked){
18559             input.checked = this.checked;
18560         }
18561         
18562         if (this.name) {
18563             input.name = this.name;
18564         }
18565         
18566         if (this.size) {
18567             input.cls += ' input-' + this.size;
18568         }
18569         
18570         var settings=this;
18571         
18572         ['xs','sm','md','lg'].map(function(size){
18573             if (settings[size]) {
18574                 cfg.cls += ' col-' + size + '-' + settings[size];
18575             }
18576         });
18577         
18578         var inputblock = input;
18579          
18580         if (this.before || this.after) {
18581             
18582             inputblock = {
18583                 cls : 'input-group',
18584                 cn :  [] 
18585             };
18586             
18587             if (this.before) {
18588                 inputblock.cn.push({
18589                     tag :'span',
18590                     cls : 'input-group-addon',
18591                     html : this.before
18592                 });
18593             }
18594             
18595             inputblock.cn.push(input);
18596             
18597             if (this.after) {
18598                 inputblock.cn.push({
18599                     tag :'span',
18600                     cls : 'input-group-addon',
18601                     html : this.after
18602                 });
18603             }
18604             
18605         }
18606         
18607         if (align ==='left' && this.fieldLabel.length) {
18608 //                Roo.log("left and has label");
18609                 cfg.cn = [
18610                     
18611                     {
18612                         tag: 'label',
18613                         'for' :  id,
18614                         cls : 'control-label col-md-' + this.labelWidth,
18615                         html : this.fieldLabel
18616                         
18617                     },
18618                     {
18619                         cls : "col-md-" + (12 - this.labelWidth), 
18620                         cn: [
18621                             inputblock
18622                         ]
18623                     }
18624                     
18625                 ];
18626         } else if ( this.fieldLabel.length) {
18627 //                Roo.log(" label");
18628                 cfg.cn = [
18629                    
18630                     {
18631                         tag: this.boxLabel ? 'span' : 'label',
18632                         'for': id,
18633                         cls: 'control-label box-input-label',
18634                         //cls : 'input-group-addon',
18635                         html : this.fieldLabel
18636                         
18637                     },
18638                     
18639                     inputblock
18640                     
18641                 ];
18642
18643         } else {
18644             
18645 //                Roo.log(" no label && no align");
18646                 cfg.cn = [  inputblock ] ;
18647                 
18648                 
18649         }
18650         if(this.boxLabel){
18651              var boxLabelCfg = {
18652                 tag: 'label',
18653                 //'for': id, // box label is handled by onclick - so no for...
18654                 cls: 'box-label',
18655                 html: this.boxLabel
18656             };
18657             
18658             if(this.tooltip){
18659                 boxLabelCfg.tooltip = this.tooltip;
18660             }
18661              
18662             cfg.cn.push(boxLabelCfg);
18663         }
18664         
18665         
18666        
18667         return cfg;
18668         
18669     },
18670     
18671     /**
18672      * return the real input element.
18673      */
18674     inputEl: function ()
18675     {
18676         return this.el.select('input.roo-' + this.inputType,true).first();
18677     },
18678     
18679     labelEl: function()
18680     {
18681         return this.el.select('label.control-label',true).first();
18682     },
18683     /* depricated... */
18684     
18685     label: function()
18686     {
18687         return this.labelEl();
18688     },
18689     
18690     initEvents : function()
18691     {
18692 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18693         
18694         this.inputEl().on('click', this.onClick,  this);
18695         
18696         if (this.boxLabel) { 
18697             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18698         }
18699         
18700         this.startValue = this.getValue();
18701         
18702         if(this.groupId){
18703             Roo.bootstrap.CheckBox.register(this);
18704         }
18705     },
18706     
18707     onClick : function()
18708     {   
18709         this.setChecked(!this.checked);
18710     },
18711     
18712     setChecked : function(state,suppressEvent)
18713     {
18714         this.startValue = this.getValue();
18715         
18716         if(this.inputType == 'radio'){
18717             
18718             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18719                 e.dom.checked = false;
18720             });
18721             
18722             this.inputEl().dom.checked = true;
18723             
18724             this.inputEl().dom.value = this.inputValue;
18725             
18726             if(suppressEvent !== true){
18727                 this.fireEvent('check', this, true);
18728             }
18729             
18730             this.validate();
18731             
18732             return;
18733         }
18734         
18735         this.checked = state;
18736         
18737         this.inputEl().dom.checked = state;
18738         
18739         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18740         
18741         if(suppressEvent !== true){
18742             this.fireEvent('check', this, state);
18743         }
18744         
18745         this.validate();
18746     },
18747     
18748     getValue : function()
18749     {
18750         if(this.inputType == 'radio'){
18751             return this.getGroupValue();
18752         }
18753         
18754         return this.inputEl().getValue();
18755         
18756     },
18757     
18758     getGroupValue : function()
18759     {
18760         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18761             return '';
18762         }
18763         
18764         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18765     },
18766     
18767     setValue : function(v,suppressEvent)
18768     {
18769         if(this.inputType == 'radio'){
18770             this.setGroupValue(v, suppressEvent);
18771             return;
18772         }
18773         
18774         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18775         
18776         this.validate();
18777     },
18778     
18779     setGroupValue : function(v, suppressEvent)
18780     {
18781         this.startValue = this.getValue();
18782         
18783         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18784             e.dom.checked = false;
18785             
18786             if(e.dom.value == v){
18787                 e.dom.checked = true;
18788             }
18789         });
18790         
18791         if(suppressEvent !== true){
18792             this.fireEvent('check', this, true);
18793         }
18794
18795         this.validate();
18796         
18797         return;
18798     },
18799     
18800     validate : function()
18801     {
18802         if(
18803                 this.disabled || 
18804                 (this.inputType == 'radio' && this.validateRadio()) ||
18805                 (this.inputType == 'checkbox' && this.validateCheckbox())
18806         ){
18807             this.markValid();
18808             return true;
18809         }
18810         
18811         this.markInvalid();
18812         return false;
18813     },
18814     
18815     validateRadio : function()
18816     {
18817         var valid = false;
18818         
18819         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18820             if(!e.dom.checked){
18821                 return;
18822             }
18823             
18824             valid = true;
18825             
18826             return false;
18827         });
18828         
18829         return valid;
18830     },
18831     
18832     validateCheckbox : function()
18833     {
18834         if(!this.groupId){
18835             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18836         }
18837         
18838         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18839         
18840         if(!group){
18841             return false;
18842         }
18843         
18844         var r = false;
18845         
18846         for(var i in group){
18847             if(r){
18848                 break;
18849             }
18850             
18851             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18852         }
18853         
18854         return r;
18855     },
18856     
18857     /**
18858      * Mark this field as valid
18859      */
18860     markValid : function()
18861     {
18862         if(this.allowBlank){
18863             return;
18864         }
18865         
18866         var _this = this;
18867         
18868         this.fireEvent('valid', this);
18869         
18870         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18871         
18872         if(this.groupId){
18873             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18874         }
18875         
18876         if(label){
18877             label.markValid();
18878         }
18879         
18880         if(this.inputType == 'radio'){
18881             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18882                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18883                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18884             });
18885             
18886             return;
18887         }
18888         
18889         if(!this.groupId){
18890             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18891             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18892             return;
18893         }
18894         
18895         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18896             
18897         if(!group){
18898             return;
18899         }
18900         
18901         for(var i in group){
18902             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18903             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18904         }
18905     },
18906     
18907      /**
18908      * Mark this field as invalid
18909      * @param {String} msg The validation message
18910      */
18911     markInvalid : function(msg)
18912     {
18913         if(this.allowBlank){
18914             return;
18915         }
18916         
18917         var _this = this;
18918         
18919         this.fireEvent('invalid', this, msg);
18920         
18921         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18922         
18923         if(this.groupId){
18924             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18925         }
18926         
18927         if(label){
18928             label.markInvalid();
18929         }
18930             
18931         if(this.inputType == 'radio'){
18932             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18933                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18934                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18935             });
18936             
18937             return;
18938         }
18939         
18940         if(!this.groupId){
18941             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18942             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18943             return;
18944         }
18945         
18946         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18947         
18948         if(!group){
18949             return;
18950         }
18951         
18952         for(var i in group){
18953             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18954             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18955         }
18956         
18957     }
18958     
18959 });
18960
18961 Roo.apply(Roo.bootstrap.CheckBox, {
18962     
18963     groups: {},
18964     
18965      /**
18966     * register a CheckBox Group
18967     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18968     */
18969     register : function(checkbox)
18970     {
18971         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18972             this.groups[checkbox.groupId] = {};
18973         }
18974         
18975         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18976             return;
18977         }
18978         
18979         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18980         
18981     },
18982     /**
18983     * fetch a CheckBox Group based on the group ID
18984     * @param {string} the group ID
18985     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18986     */
18987     get: function(groupId) {
18988         if (typeof(this.groups[groupId]) == 'undefined') {
18989             return false;
18990         }
18991         
18992         return this.groups[groupId] ;
18993     }
18994     
18995     
18996 });
18997 /*
18998  * - LGPL
18999  *
19000  * Radio
19001  *
19002  *
19003  * not inline
19004  *<div class="radio">
19005   <label>
19006     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19007     Option one is this and that&mdash;be sure to include why it's great
19008   </label>
19009 </div>
19010  *
19011  *
19012  *inline
19013  *<span>
19014  *<label class="radio-inline">fieldLabel</label>
19015  *<label class="radio-inline">
19016   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19017 </label>
19018 <span>
19019  * 
19020  * 
19021  */
19022
19023 /**
19024  * @class Roo.bootstrap.Radio
19025  * @extends Roo.bootstrap.CheckBox
19026  * Bootstrap Radio class
19027
19028  * @constructor
19029  * Create a new Radio
19030  * @param {Object} config The config object
19031  */
19032
19033 Roo.bootstrap.Radio = function(config){
19034     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19035    
19036 };
19037
19038 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19039     
19040     inputType: 'radio',
19041     inputValue: '',
19042     valueOff: '',
19043     
19044     getAutoCreate : function()
19045     {
19046         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19047         align = align || 'left'; // default...
19048         
19049         
19050         
19051         var id = Roo.id();
19052         
19053         var cfg = {
19054                 tag : this.inline ? 'span' : 'div',
19055                 cls : '',
19056                 cn : []
19057         };
19058         
19059         var inline = this.inline ? ' radio-inline' : '';
19060         
19061         var lbl = {
19062                 tag: 'label' ,
19063                 // does not need for, as we wrap the input with it..
19064                 'for' : id,
19065                 cls : 'control-label box-label' + inline,
19066                 cn : []
19067         };
19068         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19069         
19070         var fieldLabel = {
19071             tag: 'label' ,
19072             //cls : 'control-label' + inline,
19073             html : this.fieldLabel,
19074             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19075         };
19076         
19077  
19078         
19079         
19080         var input =  {
19081             tag: 'input',
19082             id : id,
19083             type : this.inputType,
19084             //value : (!this.checked) ? this.valueOff : this.inputValue,
19085             value : this.inputValue,
19086             cls : 'roo-radio',
19087             placeholder : this.placeholder || '' // ?? needed????
19088             
19089         };
19090         if (this.weight) { // Validity check?
19091             input.cls += " radio-" + this.weight;
19092         }
19093         if (this.disabled) {
19094             input.disabled=true;
19095         }
19096         
19097         if(this.checked){
19098             input.checked = this.checked;
19099         }
19100         
19101         if (this.name) {
19102             input.name = this.name;
19103         }
19104         
19105         if (this.size) {
19106             input.cls += ' input-' + this.size;
19107         }
19108         
19109         //?? can span's inline have a width??
19110         
19111         var settings=this;
19112         ['xs','sm','md','lg'].map(function(size){
19113             if (settings[size]) {
19114                 cfg.cls += ' col-' + size + '-' + settings[size];
19115             }
19116         });
19117         
19118         var inputblock = input;
19119         
19120         if (this.before || this.after) {
19121             
19122             inputblock = {
19123                 cls : 'input-group',
19124                 tag : 'span',
19125                 cn :  [] 
19126             };
19127             if (this.before) {
19128                 inputblock.cn.push({
19129                     tag :'span',
19130                     cls : 'input-group-addon',
19131                     html : this.before
19132                 });
19133             }
19134             inputblock.cn.push(input);
19135             if (this.after) {
19136                 inputblock.cn.push({
19137                     tag :'span',
19138                     cls : 'input-group-addon',
19139                     html : this.after
19140                 });
19141             }
19142             
19143         };
19144         
19145         
19146         if (this.fieldLabel && this.fieldLabel.length) {
19147             cfg.cn.push(fieldLabel);
19148         }
19149        
19150         // normal bootstrap puts the input inside the label.
19151         // however with our styled version - it has to go after the input.
19152        
19153         //lbl.cn.push(inputblock);
19154         
19155         var lblwrap =  {
19156             tag: 'span',
19157             cls: 'radio' + inline,
19158             cn: [
19159                 inputblock,
19160                 lbl
19161             ]
19162         };
19163         
19164         cfg.cn.push( lblwrap);
19165         
19166         if(this.boxLabel){
19167             lbl.cn.push({
19168                 tag: 'span',
19169                 html: this.boxLabel
19170             })
19171         }
19172          
19173         
19174         return cfg;
19175         
19176     },
19177     
19178     initEvents : function()
19179     {
19180 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19181         
19182         this.inputEl().on('click', this.onClick,  this);
19183         if (this.boxLabel) {
19184             //Roo.log('find label');
19185             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19186         }
19187         
19188     },
19189     
19190     inputEl: function ()
19191     {
19192         return this.el.select('input.roo-radio',true).first();
19193     },
19194     onClick : function()
19195     {   
19196         Roo.log("click");
19197         this.setChecked(true);
19198     },
19199     
19200     setChecked : function(state,suppressEvent)
19201     {
19202         if(state){
19203             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19204                 v.dom.checked = false;
19205             });
19206         }
19207         Roo.log(this.inputEl().dom);
19208         this.checked = state;
19209         this.inputEl().dom.checked = state;
19210         
19211         if(suppressEvent !== true){
19212             this.fireEvent('check', this, state);
19213         }
19214         
19215         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19216         
19217     },
19218     
19219     getGroupValue : function()
19220     {
19221         var value = '';
19222         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19223             if(v.dom.checked == true){
19224                 value = v.dom.value;
19225             }
19226         });
19227         
19228         return value;
19229     },
19230     
19231     /**
19232      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19233      * @return {Mixed} value The field value
19234      */
19235     getValue : function(){
19236         return this.getGroupValue();
19237     }
19238     
19239 });
19240
19241  
19242 //<script type="text/javascript">
19243
19244 /*
19245  * Based  Ext JS Library 1.1.1
19246  * Copyright(c) 2006-2007, Ext JS, LLC.
19247  * LGPL
19248  *
19249  */
19250  
19251 /**
19252  * @class Roo.HtmlEditorCore
19253  * @extends Roo.Component
19254  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19255  *
19256  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19257  */
19258
19259 Roo.HtmlEditorCore = function(config){
19260     
19261     
19262     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19263     
19264     
19265     this.addEvents({
19266         /**
19267          * @event initialize
19268          * Fires when the editor is fully initialized (including the iframe)
19269          * @param {Roo.HtmlEditorCore} this
19270          */
19271         initialize: true,
19272         /**
19273          * @event activate
19274          * Fires when the editor is first receives the focus. Any insertion must wait
19275          * until after this event.
19276          * @param {Roo.HtmlEditorCore} this
19277          */
19278         activate: true,
19279          /**
19280          * @event beforesync
19281          * Fires before the textarea is updated with content from the editor iframe. Return false
19282          * to cancel the sync.
19283          * @param {Roo.HtmlEditorCore} this
19284          * @param {String} html
19285          */
19286         beforesync: true,
19287          /**
19288          * @event beforepush
19289          * Fires before the iframe editor is updated with content from the textarea. Return false
19290          * to cancel the push.
19291          * @param {Roo.HtmlEditorCore} this
19292          * @param {String} html
19293          */
19294         beforepush: true,
19295          /**
19296          * @event sync
19297          * Fires when the textarea is updated with content from the editor iframe.
19298          * @param {Roo.HtmlEditorCore} this
19299          * @param {String} html
19300          */
19301         sync: true,
19302          /**
19303          * @event push
19304          * Fires when the iframe editor is updated with content from the textarea.
19305          * @param {Roo.HtmlEditorCore} this
19306          * @param {String} html
19307          */
19308         push: true,
19309         
19310         /**
19311          * @event editorevent
19312          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19313          * @param {Roo.HtmlEditorCore} this
19314          */
19315         editorevent: true
19316         
19317     });
19318     
19319     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19320     
19321     // defaults : white / black...
19322     this.applyBlacklists();
19323     
19324     
19325     
19326 };
19327
19328
19329 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19330
19331
19332      /**
19333      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19334      */
19335     
19336     owner : false,
19337     
19338      /**
19339      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19340      *                        Roo.resizable.
19341      */
19342     resizable : false,
19343      /**
19344      * @cfg {Number} height (in pixels)
19345      */   
19346     height: 300,
19347    /**
19348      * @cfg {Number} width (in pixels)
19349      */   
19350     width: 500,
19351     
19352     /**
19353      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19354      * 
19355      */
19356     stylesheets: false,
19357     
19358     // id of frame..
19359     frameId: false,
19360     
19361     // private properties
19362     validationEvent : false,
19363     deferHeight: true,
19364     initialized : false,
19365     activated : false,
19366     sourceEditMode : false,
19367     onFocus : Roo.emptyFn,
19368     iframePad:3,
19369     hideMode:'offsets',
19370     
19371     clearUp: true,
19372     
19373     // blacklist + whitelisted elements..
19374     black: false,
19375     white: false,
19376      
19377     
19378
19379     /**
19380      * Protected method that will not generally be called directly. It
19381      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19382      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19383      */
19384     getDocMarkup : function(){
19385         // body styles..
19386         var st = '';
19387         
19388         // inherit styels from page...?? 
19389         if (this.stylesheets === false) {
19390             
19391             Roo.get(document.head).select('style').each(function(node) {
19392                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19393             });
19394             
19395             Roo.get(document.head).select('link').each(function(node) { 
19396                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19397             });
19398             
19399         } else if (!this.stylesheets.length) {
19400                 // simple..
19401                 st = '<style type="text/css">' +
19402                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19403                    '</style>';
19404         } else { 
19405             
19406         }
19407         
19408         st +=  '<style type="text/css">' +
19409             'IMG { cursor: pointer } ' +
19410         '</style>';
19411
19412         
19413         return '<html><head>' + st  +
19414             //<style type="text/css">' +
19415             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19416             //'</style>' +
19417             ' </head><body class="roo-htmleditor-body"></body></html>';
19418     },
19419
19420     // private
19421     onRender : function(ct, position)
19422     {
19423         var _t = this;
19424         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19425         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19426         
19427         
19428         this.el.dom.style.border = '0 none';
19429         this.el.dom.setAttribute('tabIndex', -1);
19430         this.el.addClass('x-hidden hide');
19431         
19432         
19433         
19434         if(Roo.isIE){ // fix IE 1px bogus margin
19435             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19436         }
19437        
19438         
19439         this.frameId = Roo.id();
19440         
19441          
19442         
19443         var iframe = this.owner.wrap.createChild({
19444             tag: 'iframe',
19445             cls: 'form-control', // bootstrap..
19446             id: this.frameId,
19447             name: this.frameId,
19448             frameBorder : 'no',
19449             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19450         }, this.el
19451         );
19452         
19453         
19454         this.iframe = iframe.dom;
19455
19456          this.assignDocWin();
19457         
19458         this.doc.designMode = 'on';
19459        
19460         this.doc.open();
19461         this.doc.write(this.getDocMarkup());
19462         this.doc.close();
19463
19464         
19465         var task = { // must defer to wait for browser to be ready
19466             run : function(){
19467                 //console.log("run task?" + this.doc.readyState);
19468                 this.assignDocWin();
19469                 if(this.doc.body || this.doc.readyState == 'complete'){
19470                     try {
19471                         this.doc.designMode="on";
19472                     } catch (e) {
19473                         return;
19474                     }
19475                     Roo.TaskMgr.stop(task);
19476                     this.initEditor.defer(10, this);
19477                 }
19478             },
19479             interval : 10,
19480             duration: 10000,
19481             scope: this
19482         };
19483         Roo.TaskMgr.start(task);
19484
19485     },
19486
19487     // private
19488     onResize : function(w, h)
19489     {
19490          Roo.log('resize: ' +w + ',' + h );
19491         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19492         if(!this.iframe){
19493             return;
19494         }
19495         if(typeof w == 'number'){
19496             
19497             this.iframe.style.width = w + 'px';
19498         }
19499         if(typeof h == 'number'){
19500             
19501             this.iframe.style.height = h + 'px';
19502             if(this.doc){
19503                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19504             }
19505         }
19506         
19507     },
19508
19509     /**
19510      * Toggles the editor between standard and source edit mode.
19511      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19512      */
19513     toggleSourceEdit : function(sourceEditMode){
19514         
19515         this.sourceEditMode = sourceEditMode === true;
19516         
19517         if(this.sourceEditMode){
19518  
19519             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19520             
19521         }else{
19522             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19523             //this.iframe.className = '';
19524             this.deferFocus();
19525         }
19526         //this.setSize(this.owner.wrap.getSize());
19527         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19528     },
19529
19530     
19531   
19532
19533     /**
19534      * Protected method that will not generally be called directly. If you need/want
19535      * custom HTML cleanup, this is the method you should override.
19536      * @param {String} html The HTML to be cleaned
19537      * return {String} The cleaned HTML
19538      */
19539     cleanHtml : function(html){
19540         html = String(html);
19541         if(html.length > 5){
19542             if(Roo.isSafari){ // strip safari nonsense
19543                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19544             }
19545         }
19546         if(html == '&nbsp;'){
19547             html = '';
19548         }
19549         return html;
19550     },
19551
19552     /**
19553      * HTML Editor -> Textarea
19554      * Protected method that will not generally be called directly. Syncs the contents
19555      * of the editor iframe with the textarea.
19556      */
19557     syncValue : function(){
19558         if(this.initialized){
19559             var bd = (this.doc.body || this.doc.documentElement);
19560             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19561             var html = bd.innerHTML;
19562             if(Roo.isSafari){
19563                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19564                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19565                 if(m && m[1]){
19566                     html = '<div style="'+m[0]+'">' + html + '</div>';
19567                 }
19568             }
19569             html = this.cleanHtml(html);
19570             // fix up the special chars.. normaly like back quotes in word...
19571             // however we do not want to do this with chinese..
19572             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19573                 var cc = b.charCodeAt();
19574                 if (
19575                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19576                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19577                     (cc >= 0xf900 && cc < 0xfb00 )
19578                 ) {
19579                         return b;
19580                 }
19581                 return "&#"+cc+";" 
19582             });
19583             if(this.owner.fireEvent('beforesync', this, html) !== false){
19584                 this.el.dom.value = html;
19585                 this.owner.fireEvent('sync', this, html);
19586             }
19587         }
19588     },
19589
19590     /**
19591      * Protected method that will not generally be called directly. Pushes the value of the textarea
19592      * into the iframe editor.
19593      */
19594     pushValue : function(){
19595         if(this.initialized){
19596             var v = this.el.dom.value.trim();
19597             
19598 //            if(v.length < 1){
19599 //                v = '&#160;';
19600 //            }
19601             
19602             if(this.owner.fireEvent('beforepush', this, v) !== false){
19603                 var d = (this.doc.body || this.doc.documentElement);
19604                 d.innerHTML = v;
19605                 this.cleanUpPaste();
19606                 this.el.dom.value = d.innerHTML;
19607                 this.owner.fireEvent('push', this, v);
19608             }
19609         }
19610     },
19611
19612     // private
19613     deferFocus : function(){
19614         this.focus.defer(10, this);
19615     },
19616
19617     // doc'ed in Field
19618     focus : function(){
19619         if(this.win && !this.sourceEditMode){
19620             this.win.focus();
19621         }else{
19622             this.el.focus();
19623         }
19624     },
19625     
19626     assignDocWin: function()
19627     {
19628         var iframe = this.iframe;
19629         
19630          if(Roo.isIE){
19631             this.doc = iframe.contentWindow.document;
19632             this.win = iframe.contentWindow;
19633         } else {
19634 //            if (!Roo.get(this.frameId)) {
19635 //                return;
19636 //            }
19637 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19638 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19639             
19640             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19641                 return;
19642             }
19643             
19644             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19645             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19646         }
19647     },
19648     
19649     // private
19650     initEditor : function(){
19651         //console.log("INIT EDITOR");
19652         this.assignDocWin();
19653         
19654         
19655         
19656         this.doc.designMode="on";
19657         this.doc.open();
19658         this.doc.write(this.getDocMarkup());
19659         this.doc.close();
19660         
19661         var dbody = (this.doc.body || this.doc.documentElement);
19662         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19663         // this copies styles from the containing element into thsi one..
19664         // not sure why we need all of this..
19665         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19666         
19667         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19668         //ss['background-attachment'] = 'fixed'; // w3c
19669         dbody.bgProperties = 'fixed'; // ie
19670         //Roo.DomHelper.applyStyles(dbody, ss);
19671         Roo.EventManager.on(this.doc, {
19672             //'mousedown': this.onEditorEvent,
19673             'mouseup': this.onEditorEvent,
19674             'dblclick': this.onEditorEvent,
19675             'click': this.onEditorEvent,
19676             'keyup': this.onEditorEvent,
19677             buffer:100,
19678             scope: this
19679         });
19680         if(Roo.isGecko){
19681             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19682         }
19683         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19684             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19685         }
19686         this.initialized = true;
19687
19688         this.owner.fireEvent('initialize', this);
19689         this.pushValue();
19690     },
19691
19692     // private
19693     onDestroy : function(){
19694         
19695         
19696         
19697         if(this.rendered){
19698             
19699             //for (var i =0; i < this.toolbars.length;i++) {
19700             //    // fixme - ask toolbars for heights?
19701             //    this.toolbars[i].onDestroy();
19702            // }
19703             
19704             //this.wrap.dom.innerHTML = '';
19705             //this.wrap.remove();
19706         }
19707     },
19708
19709     // private
19710     onFirstFocus : function(){
19711         
19712         this.assignDocWin();
19713         
19714         
19715         this.activated = true;
19716          
19717     
19718         if(Roo.isGecko){ // prevent silly gecko errors
19719             this.win.focus();
19720             var s = this.win.getSelection();
19721             if(!s.focusNode || s.focusNode.nodeType != 3){
19722                 var r = s.getRangeAt(0);
19723                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19724                 r.collapse(true);
19725                 this.deferFocus();
19726             }
19727             try{
19728                 this.execCmd('useCSS', true);
19729                 this.execCmd('styleWithCSS', false);
19730             }catch(e){}
19731         }
19732         this.owner.fireEvent('activate', this);
19733     },
19734
19735     // private
19736     adjustFont: function(btn){
19737         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19738         //if(Roo.isSafari){ // safari
19739         //    adjust *= 2;
19740        // }
19741         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19742         if(Roo.isSafari){ // safari
19743             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19744             v =  (v < 10) ? 10 : v;
19745             v =  (v > 48) ? 48 : v;
19746             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19747             
19748         }
19749         
19750         
19751         v = Math.max(1, v+adjust);
19752         
19753         this.execCmd('FontSize', v  );
19754     },
19755
19756     onEditorEvent : function(e)
19757     {
19758         this.owner.fireEvent('editorevent', this, e);
19759       //  this.updateToolbar();
19760         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19761     },
19762
19763     insertTag : function(tg)
19764     {
19765         // could be a bit smarter... -> wrap the current selected tRoo..
19766         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19767             
19768             range = this.createRange(this.getSelection());
19769             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19770             wrappingNode.appendChild(range.extractContents());
19771             range.insertNode(wrappingNode);
19772
19773             return;
19774             
19775             
19776             
19777         }
19778         this.execCmd("formatblock",   tg);
19779         
19780     },
19781     
19782     insertText : function(txt)
19783     {
19784         
19785         
19786         var range = this.createRange();
19787         range.deleteContents();
19788                //alert(Sender.getAttribute('label'));
19789                
19790         range.insertNode(this.doc.createTextNode(txt));
19791     } ,
19792     
19793      
19794
19795     /**
19796      * Executes a Midas editor command on the editor document and performs necessary focus and
19797      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19798      * @param {String} cmd The Midas command
19799      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19800      */
19801     relayCmd : function(cmd, value){
19802         this.win.focus();
19803         this.execCmd(cmd, value);
19804         this.owner.fireEvent('editorevent', this);
19805         //this.updateToolbar();
19806         this.owner.deferFocus();
19807     },
19808
19809     /**
19810      * Executes a Midas editor command directly on the editor document.
19811      * For visual commands, you should use {@link #relayCmd} instead.
19812      * <b>This should only be called after the editor is initialized.</b>
19813      * @param {String} cmd The Midas command
19814      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19815      */
19816     execCmd : function(cmd, value){
19817         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19818         this.syncValue();
19819     },
19820  
19821  
19822    
19823     /**
19824      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19825      * to insert tRoo.
19826      * @param {String} text | dom node.. 
19827      */
19828     insertAtCursor : function(text)
19829     {
19830         
19831         
19832         
19833         if(!this.activated){
19834             return;
19835         }
19836         /*
19837         if(Roo.isIE){
19838             this.win.focus();
19839             var r = this.doc.selection.createRange();
19840             if(r){
19841                 r.collapse(true);
19842                 r.pasteHTML(text);
19843                 this.syncValue();
19844                 this.deferFocus();
19845             
19846             }
19847             return;
19848         }
19849         */
19850         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19851             this.win.focus();
19852             
19853             
19854             // from jquery ui (MIT licenced)
19855             var range, node;
19856             var win = this.win;
19857             
19858             if (win.getSelection && win.getSelection().getRangeAt) {
19859                 range = win.getSelection().getRangeAt(0);
19860                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19861                 range.insertNode(node);
19862             } else if (win.document.selection && win.document.selection.createRange) {
19863                 // no firefox support
19864                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19865                 win.document.selection.createRange().pasteHTML(txt);
19866             } else {
19867                 // no firefox support
19868                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19869                 this.execCmd('InsertHTML', txt);
19870             } 
19871             
19872             this.syncValue();
19873             
19874             this.deferFocus();
19875         }
19876     },
19877  // private
19878     mozKeyPress : function(e){
19879         if(e.ctrlKey){
19880             var c = e.getCharCode(), cmd;
19881           
19882             if(c > 0){
19883                 c = String.fromCharCode(c).toLowerCase();
19884                 switch(c){
19885                     case 'b':
19886                         cmd = 'bold';
19887                         break;
19888                     case 'i':
19889                         cmd = 'italic';
19890                         break;
19891                     
19892                     case 'u':
19893                         cmd = 'underline';
19894                         break;
19895                     
19896                     case 'v':
19897                         this.cleanUpPaste.defer(100, this);
19898                         return;
19899                         
19900                 }
19901                 if(cmd){
19902                     this.win.focus();
19903                     this.execCmd(cmd);
19904                     this.deferFocus();
19905                     e.preventDefault();
19906                 }
19907                 
19908             }
19909         }
19910     },
19911
19912     // private
19913     fixKeys : function(){ // load time branching for fastest keydown performance
19914         if(Roo.isIE){
19915             return function(e){
19916                 var k = e.getKey(), r;
19917                 if(k == e.TAB){
19918                     e.stopEvent();
19919                     r = this.doc.selection.createRange();
19920                     if(r){
19921                         r.collapse(true);
19922                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19923                         this.deferFocus();
19924                     }
19925                     return;
19926                 }
19927                 
19928                 if(k == e.ENTER){
19929                     r = this.doc.selection.createRange();
19930                     if(r){
19931                         var target = r.parentElement();
19932                         if(!target || target.tagName.toLowerCase() != 'li'){
19933                             e.stopEvent();
19934                             r.pasteHTML('<br />');
19935                             r.collapse(false);
19936                             r.select();
19937                         }
19938                     }
19939                 }
19940                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19941                     this.cleanUpPaste.defer(100, this);
19942                     return;
19943                 }
19944                 
19945                 
19946             };
19947         }else if(Roo.isOpera){
19948             return function(e){
19949                 var k = e.getKey();
19950                 if(k == e.TAB){
19951                     e.stopEvent();
19952                     this.win.focus();
19953                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19954                     this.deferFocus();
19955                 }
19956                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19957                     this.cleanUpPaste.defer(100, this);
19958                     return;
19959                 }
19960                 
19961             };
19962         }else if(Roo.isSafari){
19963             return function(e){
19964                 var k = e.getKey();
19965                 
19966                 if(k == e.TAB){
19967                     e.stopEvent();
19968                     this.execCmd('InsertText','\t');
19969                     this.deferFocus();
19970                     return;
19971                 }
19972                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19973                     this.cleanUpPaste.defer(100, this);
19974                     return;
19975                 }
19976                 
19977              };
19978         }
19979     }(),
19980     
19981     getAllAncestors: function()
19982     {
19983         var p = this.getSelectedNode();
19984         var a = [];
19985         if (!p) {
19986             a.push(p); // push blank onto stack..
19987             p = this.getParentElement();
19988         }
19989         
19990         
19991         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19992             a.push(p);
19993             p = p.parentNode;
19994         }
19995         a.push(this.doc.body);
19996         return a;
19997     },
19998     lastSel : false,
19999     lastSelNode : false,
20000     
20001     
20002     getSelection : function() 
20003     {
20004         this.assignDocWin();
20005         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20006     },
20007     
20008     getSelectedNode: function() 
20009     {
20010         // this may only work on Gecko!!!
20011         
20012         // should we cache this!!!!
20013         
20014         
20015         
20016          
20017         var range = this.createRange(this.getSelection()).cloneRange();
20018         
20019         if (Roo.isIE) {
20020             var parent = range.parentElement();
20021             while (true) {
20022                 var testRange = range.duplicate();
20023                 testRange.moveToElementText(parent);
20024                 if (testRange.inRange(range)) {
20025                     break;
20026                 }
20027                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20028                     break;
20029                 }
20030                 parent = parent.parentElement;
20031             }
20032             return parent;
20033         }
20034         
20035         // is ancestor a text element.
20036         var ac =  range.commonAncestorContainer;
20037         if (ac.nodeType == 3) {
20038             ac = ac.parentNode;
20039         }
20040         
20041         var ar = ac.childNodes;
20042          
20043         var nodes = [];
20044         var other_nodes = [];
20045         var has_other_nodes = false;
20046         for (var i=0;i<ar.length;i++) {
20047             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20048                 continue;
20049             }
20050             // fullly contained node.
20051             
20052             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20053                 nodes.push(ar[i]);
20054                 continue;
20055             }
20056             
20057             // probably selected..
20058             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20059                 other_nodes.push(ar[i]);
20060                 continue;
20061             }
20062             // outer..
20063             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20064                 continue;
20065             }
20066             
20067             
20068             has_other_nodes = true;
20069         }
20070         if (!nodes.length && other_nodes.length) {
20071             nodes= other_nodes;
20072         }
20073         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20074             return false;
20075         }
20076         
20077         return nodes[0];
20078     },
20079     createRange: function(sel)
20080     {
20081         // this has strange effects when using with 
20082         // top toolbar - not sure if it's a great idea.
20083         //this.editor.contentWindow.focus();
20084         if (typeof sel != "undefined") {
20085             try {
20086                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20087             } catch(e) {
20088                 return this.doc.createRange();
20089             }
20090         } else {
20091             return this.doc.createRange();
20092         }
20093     },
20094     getParentElement: function()
20095     {
20096         
20097         this.assignDocWin();
20098         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20099         
20100         var range = this.createRange(sel);
20101          
20102         try {
20103             var p = range.commonAncestorContainer;
20104             while (p.nodeType == 3) { // text node
20105                 p = p.parentNode;
20106             }
20107             return p;
20108         } catch (e) {
20109             return null;
20110         }
20111     
20112     },
20113     /***
20114      *
20115      * Range intersection.. the hard stuff...
20116      *  '-1' = before
20117      *  '0' = hits..
20118      *  '1' = after.
20119      *         [ -- selected range --- ]
20120      *   [fail]                        [fail]
20121      *
20122      *    basically..
20123      *      if end is before start or  hits it. fail.
20124      *      if start is after end or hits it fail.
20125      *
20126      *   if either hits (but other is outside. - then it's not 
20127      *   
20128      *    
20129      **/
20130     
20131     
20132     // @see http://www.thismuchiknow.co.uk/?p=64.
20133     rangeIntersectsNode : function(range, node)
20134     {
20135         var nodeRange = node.ownerDocument.createRange();
20136         try {
20137             nodeRange.selectNode(node);
20138         } catch (e) {
20139             nodeRange.selectNodeContents(node);
20140         }
20141     
20142         var rangeStartRange = range.cloneRange();
20143         rangeStartRange.collapse(true);
20144     
20145         var rangeEndRange = range.cloneRange();
20146         rangeEndRange.collapse(false);
20147     
20148         var nodeStartRange = nodeRange.cloneRange();
20149         nodeStartRange.collapse(true);
20150     
20151         var nodeEndRange = nodeRange.cloneRange();
20152         nodeEndRange.collapse(false);
20153     
20154         return rangeStartRange.compareBoundaryPoints(
20155                  Range.START_TO_START, nodeEndRange) == -1 &&
20156                rangeEndRange.compareBoundaryPoints(
20157                  Range.START_TO_START, nodeStartRange) == 1;
20158         
20159          
20160     },
20161     rangeCompareNode : function(range, node)
20162     {
20163         var nodeRange = node.ownerDocument.createRange();
20164         try {
20165             nodeRange.selectNode(node);
20166         } catch (e) {
20167             nodeRange.selectNodeContents(node);
20168         }
20169         
20170         
20171         range.collapse(true);
20172     
20173         nodeRange.collapse(true);
20174      
20175         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20176         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20177          
20178         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20179         
20180         var nodeIsBefore   =  ss == 1;
20181         var nodeIsAfter    = ee == -1;
20182         
20183         if (nodeIsBefore && nodeIsAfter) {
20184             return 0; // outer
20185         }
20186         if (!nodeIsBefore && nodeIsAfter) {
20187             return 1; //right trailed.
20188         }
20189         
20190         if (nodeIsBefore && !nodeIsAfter) {
20191             return 2;  // left trailed.
20192         }
20193         // fully contined.
20194         return 3;
20195     },
20196
20197     // private? - in a new class?
20198     cleanUpPaste :  function()
20199     {
20200         // cleans up the whole document..
20201         Roo.log('cleanuppaste');
20202         
20203         this.cleanUpChildren(this.doc.body);
20204         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20205         if (clean != this.doc.body.innerHTML) {
20206             this.doc.body.innerHTML = clean;
20207         }
20208         
20209     },
20210     
20211     cleanWordChars : function(input) {// change the chars to hex code
20212         var he = Roo.HtmlEditorCore;
20213         
20214         var output = input;
20215         Roo.each(he.swapCodes, function(sw) { 
20216             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20217             
20218             output = output.replace(swapper, sw[1]);
20219         });
20220         
20221         return output;
20222     },
20223     
20224     
20225     cleanUpChildren : function (n)
20226     {
20227         if (!n.childNodes.length) {
20228             return;
20229         }
20230         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20231            this.cleanUpChild(n.childNodes[i]);
20232         }
20233     },
20234     
20235     
20236         
20237     
20238     cleanUpChild : function (node)
20239     {
20240         var ed = this;
20241         //console.log(node);
20242         if (node.nodeName == "#text") {
20243             // clean up silly Windows -- stuff?
20244             return; 
20245         }
20246         if (node.nodeName == "#comment") {
20247             node.parentNode.removeChild(node);
20248             // clean up silly Windows -- stuff?
20249             return; 
20250         }
20251         var lcname = node.tagName.toLowerCase();
20252         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20253         // whitelist of tags..
20254         
20255         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20256             // remove node.
20257             node.parentNode.removeChild(node);
20258             return;
20259             
20260         }
20261         
20262         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20263         
20264         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20265         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20266         
20267         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20268         //    remove_keep_children = true;
20269         //}
20270         
20271         if (remove_keep_children) {
20272             this.cleanUpChildren(node);
20273             // inserts everything just before this node...
20274             while (node.childNodes.length) {
20275                 var cn = node.childNodes[0];
20276                 node.removeChild(cn);
20277                 node.parentNode.insertBefore(cn, node);
20278             }
20279             node.parentNode.removeChild(node);
20280             return;
20281         }
20282         
20283         if (!node.attributes || !node.attributes.length) {
20284             this.cleanUpChildren(node);
20285             return;
20286         }
20287         
20288         function cleanAttr(n,v)
20289         {
20290             
20291             if (v.match(/^\./) || v.match(/^\//)) {
20292                 return;
20293             }
20294             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20295                 return;
20296             }
20297             if (v.match(/^#/)) {
20298                 return;
20299             }
20300 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20301             node.removeAttribute(n);
20302             
20303         }
20304         
20305         var cwhite = this.cwhite;
20306         var cblack = this.cblack;
20307             
20308         function cleanStyle(n,v)
20309         {
20310             if (v.match(/expression/)) { //XSS?? should we even bother..
20311                 node.removeAttribute(n);
20312                 return;
20313             }
20314             
20315             var parts = v.split(/;/);
20316             var clean = [];
20317             
20318             Roo.each(parts, function(p) {
20319                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20320                 if (!p.length) {
20321                     return true;
20322                 }
20323                 var l = p.split(':').shift().replace(/\s+/g,'');
20324                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20325                 
20326                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20327 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20328                     //node.removeAttribute(n);
20329                     return true;
20330                 }
20331                 //Roo.log()
20332                 // only allow 'c whitelisted system attributes'
20333                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20334 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20335                     //node.removeAttribute(n);
20336                     return true;
20337                 }
20338                 
20339                 
20340                  
20341                 
20342                 clean.push(p);
20343                 return true;
20344             });
20345             if (clean.length) { 
20346                 node.setAttribute(n, clean.join(';'));
20347             } else {
20348                 node.removeAttribute(n);
20349             }
20350             
20351         }
20352         
20353         
20354         for (var i = node.attributes.length-1; i > -1 ; i--) {
20355             var a = node.attributes[i];
20356             //console.log(a);
20357             
20358             if (a.name.toLowerCase().substr(0,2)=='on')  {
20359                 node.removeAttribute(a.name);
20360                 continue;
20361             }
20362             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20363                 node.removeAttribute(a.name);
20364                 continue;
20365             }
20366             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20367                 cleanAttr(a.name,a.value); // fixme..
20368                 continue;
20369             }
20370             if (a.name == 'style') {
20371                 cleanStyle(a.name,a.value);
20372                 continue;
20373             }
20374             /// clean up MS crap..
20375             // tecnically this should be a list of valid class'es..
20376             
20377             
20378             if (a.name == 'class') {
20379                 if (a.value.match(/^Mso/)) {
20380                     node.className = '';
20381                 }
20382                 
20383                 if (a.value.match(/body/)) {
20384                     node.className = '';
20385                 }
20386                 continue;
20387             }
20388             
20389             // style cleanup!?
20390             // class cleanup?
20391             
20392         }
20393         
20394         
20395         this.cleanUpChildren(node);
20396         
20397         
20398     },
20399     
20400     /**
20401      * Clean up MS wordisms...
20402      */
20403     cleanWord : function(node)
20404     {
20405         
20406         
20407         if (!node) {
20408             this.cleanWord(this.doc.body);
20409             return;
20410         }
20411         if (node.nodeName == "#text") {
20412             // clean up silly Windows -- stuff?
20413             return; 
20414         }
20415         if (node.nodeName == "#comment") {
20416             node.parentNode.removeChild(node);
20417             // clean up silly Windows -- stuff?
20418             return; 
20419         }
20420         
20421         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20422             node.parentNode.removeChild(node);
20423             return;
20424         }
20425         
20426         // remove - but keep children..
20427         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20428             while (node.childNodes.length) {
20429                 var cn = node.childNodes[0];
20430                 node.removeChild(cn);
20431                 node.parentNode.insertBefore(cn, node);
20432             }
20433             node.parentNode.removeChild(node);
20434             this.iterateChildren(node, this.cleanWord);
20435             return;
20436         }
20437         // clean styles
20438         if (node.className.length) {
20439             
20440             var cn = node.className.split(/\W+/);
20441             var cna = [];
20442             Roo.each(cn, function(cls) {
20443                 if (cls.match(/Mso[a-zA-Z]+/)) {
20444                     return;
20445                 }
20446                 cna.push(cls);
20447             });
20448             node.className = cna.length ? cna.join(' ') : '';
20449             if (!cna.length) {
20450                 node.removeAttribute("class");
20451             }
20452         }
20453         
20454         if (node.hasAttribute("lang")) {
20455             node.removeAttribute("lang");
20456         }
20457         
20458         if (node.hasAttribute("style")) {
20459             
20460             var styles = node.getAttribute("style").split(";");
20461             var nstyle = [];
20462             Roo.each(styles, function(s) {
20463                 if (!s.match(/:/)) {
20464                     return;
20465                 }
20466                 var kv = s.split(":");
20467                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20468                     return;
20469                 }
20470                 // what ever is left... we allow.
20471                 nstyle.push(s);
20472             });
20473             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20474             if (!nstyle.length) {
20475                 node.removeAttribute('style');
20476             }
20477         }
20478         this.iterateChildren(node, this.cleanWord);
20479         
20480         
20481         
20482     },
20483     /**
20484      * iterateChildren of a Node, calling fn each time, using this as the scole..
20485      * @param {DomNode} node node to iterate children of.
20486      * @param {Function} fn method of this class to call on each item.
20487      */
20488     iterateChildren : function(node, fn)
20489     {
20490         if (!node.childNodes.length) {
20491                 return;
20492         }
20493         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20494            fn.call(this, node.childNodes[i])
20495         }
20496     },
20497     
20498     
20499     /**
20500      * cleanTableWidths.
20501      *
20502      * Quite often pasting from word etc.. results in tables with column and widths.
20503      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20504      *
20505      */
20506     cleanTableWidths : function(node)
20507     {
20508          
20509          
20510         if (!node) {
20511             this.cleanTableWidths(this.doc.body);
20512             return;
20513         }
20514         
20515         // ignore list...
20516         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20517             return; 
20518         }
20519         Roo.log(node.tagName);
20520         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20521             this.iterateChildren(node, this.cleanTableWidths);
20522             return;
20523         }
20524         if (node.hasAttribute('width')) {
20525             node.removeAttribute('width');
20526         }
20527         
20528          
20529         if (node.hasAttribute("style")) {
20530             // pretty basic...
20531             
20532             var styles = node.getAttribute("style").split(";");
20533             var nstyle = [];
20534             Roo.each(styles, function(s) {
20535                 if (!s.match(/:/)) {
20536                     return;
20537                 }
20538                 var kv = s.split(":");
20539                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20540                     return;
20541                 }
20542                 // what ever is left... we allow.
20543                 nstyle.push(s);
20544             });
20545             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20546             if (!nstyle.length) {
20547                 node.removeAttribute('style');
20548             }
20549         }
20550         
20551         this.iterateChildren(node, this.cleanTableWidths);
20552         
20553         
20554     },
20555     
20556     
20557     
20558     
20559     domToHTML : function(currentElement, depth, nopadtext) {
20560         
20561         depth = depth || 0;
20562         nopadtext = nopadtext || false;
20563     
20564         if (!currentElement) {
20565             return this.domToHTML(this.doc.body);
20566         }
20567         
20568         //Roo.log(currentElement);
20569         var j;
20570         var allText = false;
20571         var nodeName = currentElement.nodeName;
20572         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20573         
20574         if  (nodeName == '#text') {
20575             
20576             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20577         }
20578         
20579         
20580         var ret = '';
20581         if (nodeName != 'BODY') {
20582              
20583             var i = 0;
20584             // Prints the node tagName, such as <A>, <IMG>, etc
20585             if (tagName) {
20586                 var attr = [];
20587                 for(i = 0; i < currentElement.attributes.length;i++) {
20588                     // quoting?
20589                     var aname = currentElement.attributes.item(i).name;
20590                     if (!currentElement.attributes.item(i).value.length) {
20591                         continue;
20592                     }
20593                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20594                 }
20595                 
20596                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20597             } 
20598             else {
20599                 
20600                 // eack
20601             }
20602         } else {
20603             tagName = false;
20604         }
20605         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20606             return ret;
20607         }
20608         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20609             nopadtext = true;
20610         }
20611         
20612         
20613         // Traverse the tree
20614         i = 0;
20615         var currentElementChild = currentElement.childNodes.item(i);
20616         var allText = true;
20617         var innerHTML  = '';
20618         lastnode = '';
20619         while (currentElementChild) {
20620             // Formatting code (indent the tree so it looks nice on the screen)
20621             var nopad = nopadtext;
20622             if (lastnode == 'SPAN') {
20623                 nopad  = true;
20624             }
20625             // text
20626             if  (currentElementChild.nodeName == '#text') {
20627                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20628                 toadd = nopadtext ? toadd : toadd.trim();
20629                 if (!nopad && toadd.length > 80) {
20630                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20631                 }
20632                 innerHTML  += toadd;
20633                 
20634                 i++;
20635                 currentElementChild = currentElement.childNodes.item(i);
20636                 lastNode = '';
20637                 continue;
20638             }
20639             allText = false;
20640             
20641             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20642                 
20643             // Recursively traverse the tree structure of the child node
20644             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20645             lastnode = currentElementChild.nodeName;
20646             i++;
20647             currentElementChild=currentElement.childNodes.item(i);
20648         }
20649         
20650         ret += innerHTML;
20651         
20652         if (!allText) {
20653                 // The remaining code is mostly for formatting the tree
20654             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20655         }
20656         
20657         
20658         if (tagName) {
20659             ret+= "</"+tagName+">";
20660         }
20661         return ret;
20662         
20663     },
20664         
20665     applyBlacklists : function()
20666     {
20667         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20668         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20669         
20670         this.white = [];
20671         this.black = [];
20672         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20673             if (b.indexOf(tag) > -1) {
20674                 return;
20675             }
20676             this.white.push(tag);
20677             
20678         }, this);
20679         
20680         Roo.each(w, function(tag) {
20681             if (b.indexOf(tag) > -1) {
20682                 return;
20683             }
20684             if (this.white.indexOf(tag) > -1) {
20685                 return;
20686             }
20687             this.white.push(tag);
20688             
20689         }, this);
20690         
20691         
20692         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20693             if (w.indexOf(tag) > -1) {
20694                 return;
20695             }
20696             this.black.push(tag);
20697             
20698         }, this);
20699         
20700         Roo.each(b, function(tag) {
20701             if (w.indexOf(tag) > -1) {
20702                 return;
20703             }
20704             if (this.black.indexOf(tag) > -1) {
20705                 return;
20706             }
20707             this.black.push(tag);
20708             
20709         }, this);
20710         
20711         
20712         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20713         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20714         
20715         this.cwhite = [];
20716         this.cblack = [];
20717         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20718             if (b.indexOf(tag) > -1) {
20719                 return;
20720             }
20721             this.cwhite.push(tag);
20722             
20723         }, this);
20724         
20725         Roo.each(w, function(tag) {
20726             if (b.indexOf(tag) > -1) {
20727                 return;
20728             }
20729             if (this.cwhite.indexOf(tag) > -1) {
20730                 return;
20731             }
20732             this.cwhite.push(tag);
20733             
20734         }, this);
20735         
20736         
20737         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20738             if (w.indexOf(tag) > -1) {
20739                 return;
20740             }
20741             this.cblack.push(tag);
20742             
20743         }, this);
20744         
20745         Roo.each(b, function(tag) {
20746             if (w.indexOf(tag) > -1) {
20747                 return;
20748             }
20749             if (this.cblack.indexOf(tag) > -1) {
20750                 return;
20751             }
20752             this.cblack.push(tag);
20753             
20754         }, this);
20755     },
20756     
20757     setStylesheets : function(stylesheets)
20758     {
20759         if(typeof(stylesheets) == 'string'){
20760             Roo.get(this.iframe.contentDocument.head).createChild({
20761                 tag : 'link',
20762                 rel : 'stylesheet',
20763                 type : 'text/css',
20764                 href : stylesheets
20765             });
20766             
20767             return;
20768         }
20769         var _this = this;
20770      
20771         Roo.each(stylesheets, function(s) {
20772             if(!s.length){
20773                 return;
20774             }
20775             
20776             Roo.get(_this.iframe.contentDocument.head).createChild({
20777                 tag : 'link',
20778                 rel : 'stylesheet',
20779                 type : 'text/css',
20780                 href : s
20781             });
20782         });
20783
20784         
20785     },
20786     
20787     removeStylesheets : function()
20788     {
20789         var _this = this;
20790         
20791         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20792             s.remove();
20793         });
20794     }
20795     
20796     // hide stuff that is not compatible
20797     /**
20798      * @event blur
20799      * @hide
20800      */
20801     /**
20802      * @event change
20803      * @hide
20804      */
20805     /**
20806      * @event focus
20807      * @hide
20808      */
20809     /**
20810      * @event specialkey
20811      * @hide
20812      */
20813     /**
20814      * @cfg {String} fieldClass @hide
20815      */
20816     /**
20817      * @cfg {String} focusClass @hide
20818      */
20819     /**
20820      * @cfg {String} autoCreate @hide
20821      */
20822     /**
20823      * @cfg {String} inputType @hide
20824      */
20825     /**
20826      * @cfg {String} invalidClass @hide
20827      */
20828     /**
20829      * @cfg {String} invalidText @hide
20830      */
20831     /**
20832      * @cfg {String} msgFx @hide
20833      */
20834     /**
20835      * @cfg {String} validateOnBlur @hide
20836      */
20837 });
20838
20839 Roo.HtmlEditorCore.white = [
20840         'area', 'br', 'img', 'input', 'hr', 'wbr',
20841         
20842        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20843        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20844        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20845        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20846        'table',   'ul',         'xmp', 
20847        
20848        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20849       'thead',   'tr', 
20850      
20851       'dir', 'menu', 'ol', 'ul', 'dl',
20852        
20853       'embed',  'object'
20854 ];
20855
20856
20857 Roo.HtmlEditorCore.black = [
20858     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20859         'applet', // 
20860         'base',   'basefont', 'bgsound', 'blink',  'body', 
20861         'frame',  'frameset', 'head',    'html',   'ilayer', 
20862         'iframe', 'layer',  'link',     'meta',    'object',   
20863         'script', 'style' ,'title',  'xml' // clean later..
20864 ];
20865 Roo.HtmlEditorCore.clean = [
20866     'script', 'style', 'title', 'xml'
20867 ];
20868 Roo.HtmlEditorCore.remove = [
20869     'font'
20870 ];
20871 // attributes..
20872
20873 Roo.HtmlEditorCore.ablack = [
20874     'on'
20875 ];
20876     
20877 Roo.HtmlEditorCore.aclean = [ 
20878     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20879 ];
20880
20881 // protocols..
20882 Roo.HtmlEditorCore.pwhite= [
20883         'http',  'https',  'mailto'
20884 ];
20885
20886 // white listed style attributes.
20887 Roo.HtmlEditorCore.cwhite= [
20888       //  'text-align', /// default is to allow most things..
20889       
20890          
20891 //        'font-size'//??
20892 ];
20893
20894 // black listed style attributes.
20895 Roo.HtmlEditorCore.cblack= [
20896       //  'font-size' -- this can be set by the project 
20897 ];
20898
20899
20900 Roo.HtmlEditorCore.swapCodes   =[ 
20901     [    8211, "--" ], 
20902     [    8212, "--" ], 
20903     [    8216,  "'" ],  
20904     [    8217, "'" ],  
20905     [    8220, '"' ],  
20906     [    8221, '"' ],  
20907     [    8226, "*" ],  
20908     [    8230, "..." ]
20909 ]; 
20910
20911     /*
20912  * - LGPL
20913  *
20914  * HtmlEditor
20915  * 
20916  */
20917
20918 /**
20919  * @class Roo.bootstrap.HtmlEditor
20920  * @extends Roo.bootstrap.TextArea
20921  * Bootstrap HtmlEditor class
20922
20923  * @constructor
20924  * Create a new HtmlEditor
20925  * @param {Object} config The config object
20926  */
20927
20928 Roo.bootstrap.HtmlEditor = function(config){
20929     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20930     if (!this.toolbars) {
20931         this.toolbars = [];
20932     }
20933     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20934     this.addEvents({
20935             /**
20936              * @event initialize
20937              * Fires when the editor is fully initialized (including the iframe)
20938              * @param {HtmlEditor} this
20939              */
20940             initialize: true,
20941             /**
20942              * @event activate
20943              * Fires when the editor is first receives the focus. Any insertion must wait
20944              * until after this event.
20945              * @param {HtmlEditor} this
20946              */
20947             activate: true,
20948              /**
20949              * @event beforesync
20950              * Fires before the textarea is updated with content from the editor iframe. Return false
20951              * to cancel the sync.
20952              * @param {HtmlEditor} this
20953              * @param {String} html
20954              */
20955             beforesync: true,
20956              /**
20957              * @event beforepush
20958              * Fires before the iframe editor is updated with content from the textarea. Return false
20959              * to cancel the push.
20960              * @param {HtmlEditor} this
20961              * @param {String} html
20962              */
20963             beforepush: true,
20964              /**
20965              * @event sync
20966              * Fires when the textarea is updated with content from the editor iframe.
20967              * @param {HtmlEditor} this
20968              * @param {String} html
20969              */
20970             sync: true,
20971              /**
20972              * @event push
20973              * Fires when the iframe editor is updated with content from the textarea.
20974              * @param {HtmlEditor} this
20975              * @param {String} html
20976              */
20977             push: true,
20978              /**
20979              * @event editmodechange
20980              * Fires when the editor switches edit modes
20981              * @param {HtmlEditor} this
20982              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20983              */
20984             editmodechange: true,
20985             /**
20986              * @event editorevent
20987              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20988              * @param {HtmlEditor} this
20989              */
20990             editorevent: true,
20991             /**
20992              * @event firstfocus
20993              * Fires when on first focus - needed by toolbars..
20994              * @param {HtmlEditor} this
20995              */
20996             firstfocus: true,
20997             /**
20998              * @event autosave
20999              * Auto save the htmlEditor value as a file into Events
21000              * @param {HtmlEditor} this
21001              */
21002             autosave: true,
21003             /**
21004              * @event savedpreview
21005              * preview the saved version of htmlEditor
21006              * @param {HtmlEditor} this
21007              */
21008             savedpreview: true
21009         });
21010 };
21011
21012
21013 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21014     
21015     
21016       /**
21017      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21018      */
21019     toolbars : false,
21020    
21021      /**
21022      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21023      *                        Roo.resizable.
21024      */
21025     resizable : false,
21026      /**
21027      * @cfg {Number} height (in pixels)
21028      */   
21029     height: 300,
21030    /**
21031      * @cfg {Number} width (in pixels)
21032      */   
21033     width: false,
21034     
21035     /**
21036      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21037      * 
21038      */
21039     stylesheets: false,
21040     
21041     // id of frame..
21042     frameId: false,
21043     
21044     // private properties
21045     validationEvent : false,
21046     deferHeight: true,
21047     initialized : false,
21048     activated : false,
21049     
21050     onFocus : Roo.emptyFn,
21051     iframePad:3,
21052     hideMode:'offsets',
21053     
21054     
21055     tbContainer : false,
21056     
21057     toolbarContainer :function() {
21058         return this.wrap.select('.x-html-editor-tb',true).first();
21059     },
21060
21061     /**
21062      * Protected method that will not generally be called directly. It
21063      * is called when the editor creates its toolbar. Override this method if you need to
21064      * add custom toolbar buttons.
21065      * @param {HtmlEditor} editor
21066      */
21067     createToolbar : function(){
21068         
21069         Roo.log("create toolbars");
21070         
21071         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21072         this.toolbars[0].render(this.toolbarContainer());
21073         
21074         return;
21075         
21076 //        if (!editor.toolbars || !editor.toolbars.length) {
21077 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21078 //        }
21079 //        
21080 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21081 //            editor.toolbars[i] = Roo.factory(
21082 //                    typeof(editor.toolbars[i]) == 'string' ?
21083 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21084 //                Roo.bootstrap.HtmlEditor);
21085 //            editor.toolbars[i].init(editor);
21086 //        }
21087     },
21088
21089      
21090     // private
21091     onRender : function(ct, position)
21092     {
21093        // Roo.log("Call onRender: " + this.xtype);
21094         var _t = this;
21095         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21096       
21097         this.wrap = this.inputEl().wrap({
21098             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21099         });
21100         
21101         this.editorcore.onRender(ct, position);
21102          
21103         if (this.resizable) {
21104             this.resizeEl = new Roo.Resizable(this.wrap, {
21105                 pinned : true,
21106                 wrap: true,
21107                 dynamic : true,
21108                 minHeight : this.height,
21109                 height: this.height,
21110                 handles : this.resizable,
21111                 width: this.width,
21112                 listeners : {
21113                     resize : function(r, w, h) {
21114                         _t.onResize(w,h); // -something
21115                     }
21116                 }
21117             });
21118             
21119         }
21120         this.createToolbar(this);
21121        
21122         
21123         if(!this.width && this.resizable){
21124             this.setSize(this.wrap.getSize());
21125         }
21126         if (this.resizeEl) {
21127             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21128             // should trigger onReize..
21129         }
21130         
21131     },
21132
21133     // private
21134     onResize : function(w, h)
21135     {
21136         Roo.log('resize: ' +w + ',' + h );
21137         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21138         var ew = false;
21139         var eh = false;
21140         
21141         if(this.inputEl() ){
21142             if(typeof w == 'number'){
21143                 var aw = w - this.wrap.getFrameWidth('lr');
21144                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21145                 ew = aw;
21146             }
21147             if(typeof h == 'number'){
21148                  var tbh = -11;  // fixme it needs to tool bar size!
21149                 for (var i =0; i < this.toolbars.length;i++) {
21150                     // fixme - ask toolbars for heights?
21151                     tbh += this.toolbars[i].el.getHeight();
21152                     //if (this.toolbars[i].footer) {
21153                     //    tbh += this.toolbars[i].footer.el.getHeight();
21154                     //}
21155                 }
21156               
21157                 
21158                 
21159                 
21160                 
21161                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21162                 ah -= 5; // knock a few pixes off for look..
21163                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21164                 var eh = ah;
21165             }
21166         }
21167         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21168         this.editorcore.onResize(ew,eh);
21169         
21170     },
21171
21172     /**
21173      * Toggles the editor between standard and source edit mode.
21174      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21175      */
21176     toggleSourceEdit : function(sourceEditMode)
21177     {
21178         this.editorcore.toggleSourceEdit(sourceEditMode);
21179         
21180         if(this.editorcore.sourceEditMode){
21181             Roo.log('editor - showing textarea');
21182             
21183 //            Roo.log('in');
21184 //            Roo.log(this.syncValue());
21185             this.syncValue();
21186             this.inputEl().removeClass(['hide', 'x-hidden']);
21187             this.inputEl().dom.removeAttribute('tabIndex');
21188             this.inputEl().focus();
21189         }else{
21190             Roo.log('editor - hiding textarea');
21191 //            Roo.log('out')
21192 //            Roo.log(this.pushValue()); 
21193             this.pushValue();
21194             
21195             this.inputEl().addClass(['hide', 'x-hidden']);
21196             this.inputEl().dom.setAttribute('tabIndex', -1);
21197             //this.deferFocus();
21198         }
21199          
21200         if(this.resizable){
21201             this.setSize(this.wrap.getSize());
21202         }
21203         
21204         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21205     },
21206  
21207     // private (for BoxComponent)
21208     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21209
21210     // private (for BoxComponent)
21211     getResizeEl : function(){
21212         return this.wrap;
21213     },
21214
21215     // private (for BoxComponent)
21216     getPositionEl : function(){
21217         return this.wrap;
21218     },
21219
21220     // private
21221     initEvents : function(){
21222         this.originalValue = this.getValue();
21223     },
21224
21225 //    /**
21226 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21227 //     * @method
21228 //     */
21229 //    markInvalid : Roo.emptyFn,
21230 //    /**
21231 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21232 //     * @method
21233 //     */
21234 //    clearInvalid : Roo.emptyFn,
21235
21236     setValue : function(v){
21237         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21238         this.editorcore.pushValue();
21239     },
21240
21241      
21242     // private
21243     deferFocus : function(){
21244         this.focus.defer(10, this);
21245     },
21246
21247     // doc'ed in Field
21248     focus : function(){
21249         this.editorcore.focus();
21250         
21251     },
21252       
21253
21254     // private
21255     onDestroy : function(){
21256         
21257         
21258         
21259         if(this.rendered){
21260             
21261             for (var i =0; i < this.toolbars.length;i++) {
21262                 // fixme - ask toolbars for heights?
21263                 this.toolbars[i].onDestroy();
21264             }
21265             
21266             this.wrap.dom.innerHTML = '';
21267             this.wrap.remove();
21268         }
21269     },
21270
21271     // private
21272     onFirstFocus : function(){
21273         //Roo.log("onFirstFocus");
21274         this.editorcore.onFirstFocus();
21275          for (var i =0; i < this.toolbars.length;i++) {
21276             this.toolbars[i].onFirstFocus();
21277         }
21278         
21279     },
21280     
21281     // private
21282     syncValue : function()
21283     {   
21284         this.editorcore.syncValue();
21285     },
21286     
21287     pushValue : function()
21288     {   
21289         this.editorcore.pushValue();
21290     }
21291      
21292     
21293     // hide stuff that is not compatible
21294     /**
21295      * @event blur
21296      * @hide
21297      */
21298     /**
21299      * @event change
21300      * @hide
21301      */
21302     /**
21303      * @event focus
21304      * @hide
21305      */
21306     /**
21307      * @event specialkey
21308      * @hide
21309      */
21310     /**
21311      * @cfg {String} fieldClass @hide
21312      */
21313     /**
21314      * @cfg {String} focusClass @hide
21315      */
21316     /**
21317      * @cfg {String} autoCreate @hide
21318      */
21319     /**
21320      * @cfg {String} inputType @hide
21321      */
21322     /**
21323      * @cfg {String} invalidClass @hide
21324      */
21325     /**
21326      * @cfg {String} invalidText @hide
21327      */
21328     /**
21329      * @cfg {String} msgFx @hide
21330      */
21331     /**
21332      * @cfg {String} validateOnBlur @hide
21333      */
21334 });
21335  
21336     
21337    
21338    
21339    
21340       
21341 Roo.namespace('Roo.bootstrap.htmleditor');
21342 /**
21343  * @class Roo.bootstrap.HtmlEditorToolbar1
21344  * Basic Toolbar
21345  * 
21346  * Usage:
21347  *
21348  new Roo.bootstrap.HtmlEditor({
21349     ....
21350     toolbars : [
21351         new Roo.bootstrap.HtmlEditorToolbar1({
21352             disable : { fonts: 1 , format: 1, ..., ... , ...],
21353             btns : [ .... ]
21354         })
21355     }
21356      
21357  * 
21358  * @cfg {Object} disable List of elements to disable..
21359  * @cfg {Array} btns List of additional buttons.
21360  * 
21361  * 
21362  * NEEDS Extra CSS? 
21363  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21364  */
21365  
21366 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21367 {
21368     
21369     Roo.apply(this, config);
21370     
21371     // default disabled, based on 'good practice'..
21372     this.disable = this.disable || {};
21373     Roo.applyIf(this.disable, {
21374         fontSize : true,
21375         colors : true,
21376         specialElements : true
21377     });
21378     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21379     
21380     this.editor = config.editor;
21381     this.editorcore = config.editor.editorcore;
21382     
21383     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21384     
21385     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21386     // dont call parent... till later.
21387 }
21388 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21389      
21390     bar : true,
21391     
21392     editor : false,
21393     editorcore : false,
21394     
21395     
21396     formats : [
21397         "p" ,  
21398         "h1","h2","h3","h4","h5","h6", 
21399         "pre", "code", 
21400         "abbr", "acronym", "address", "cite", "samp", "var",
21401         'div','span'
21402     ],
21403     
21404     onRender : function(ct, position)
21405     {
21406        // Roo.log("Call onRender: " + this.xtype);
21407         
21408        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21409        Roo.log(this.el);
21410        this.el.dom.style.marginBottom = '0';
21411        var _this = this;
21412        var editorcore = this.editorcore;
21413        var editor= this.editor;
21414        
21415        var children = [];
21416        var btn = function(id,cmd , toggle, handler){
21417        
21418             var  event = toggle ? 'toggle' : 'click';
21419        
21420             var a = {
21421                 size : 'sm',
21422                 xtype: 'Button',
21423                 xns: Roo.bootstrap,
21424                 glyphicon : id,
21425                 cmd : id || cmd,
21426                 enableToggle:toggle !== false,
21427                 //html : 'submit'
21428                 pressed : toggle ? false : null,
21429                 listeners : {}
21430             };
21431             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21432                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21433             };
21434             children.push(a);
21435             return a;
21436        }
21437         
21438         var style = {
21439                 xtype: 'Button',
21440                 size : 'sm',
21441                 xns: Roo.bootstrap,
21442                 glyphicon : 'font',
21443                 //html : 'submit'
21444                 menu : {
21445                     xtype: 'Menu',
21446                     xns: Roo.bootstrap,
21447                     items:  []
21448                 }
21449         };
21450         Roo.each(this.formats, function(f) {
21451             style.menu.items.push({
21452                 xtype :'MenuItem',
21453                 xns: Roo.bootstrap,
21454                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21455                 tagname : f,
21456                 listeners : {
21457                     click : function()
21458                     {
21459                         editorcore.insertTag(this.tagname);
21460                         editor.focus();
21461                     }
21462                 }
21463                 
21464             });
21465         });
21466          children.push(style);   
21467             
21468             
21469         btn('bold',false,true);
21470         btn('italic',false,true);
21471         btn('align-left', 'justifyleft',true);
21472         btn('align-center', 'justifycenter',true);
21473         btn('align-right' , 'justifyright',true);
21474         btn('link', false, false, function(btn) {
21475             //Roo.log("create link?");
21476             var url = prompt(this.createLinkText, this.defaultLinkValue);
21477             if(url && url != 'http:/'+'/'){
21478                 this.editorcore.relayCmd('createlink', url);
21479             }
21480         }),
21481         btn('list','insertunorderedlist',true);
21482         btn('pencil', false,true, function(btn){
21483                 Roo.log(this);
21484                 
21485                 this.toggleSourceEdit(btn.pressed);
21486         });
21487         /*
21488         var cog = {
21489                 xtype: 'Button',
21490                 size : 'sm',
21491                 xns: Roo.bootstrap,
21492                 glyphicon : 'cog',
21493                 //html : 'submit'
21494                 menu : {
21495                     xtype: 'Menu',
21496                     xns: Roo.bootstrap,
21497                     items:  []
21498                 }
21499         };
21500         
21501         cog.menu.items.push({
21502             xtype :'MenuItem',
21503             xns: Roo.bootstrap,
21504             html : Clean styles,
21505             tagname : f,
21506             listeners : {
21507                 click : function()
21508                 {
21509                     editorcore.insertTag(this.tagname);
21510                     editor.focus();
21511                 }
21512             }
21513             
21514         });
21515        */
21516         
21517          
21518        this.xtype = 'NavSimplebar';
21519         
21520         for(var i=0;i< children.length;i++) {
21521             
21522             this.buttons.add(this.addxtypeChild(children[i]));
21523             
21524         }
21525         
21526         editor.on('editorevent', this.updateToolbar, this);
21527     },
21528     onBtnClick : function(id)
21529     {
21530        this.editorcore.relayCmd(id);
21531        this.editorcore.focus();
21532     },
21533     
21534     /**
21535      * Protected method that will not generally be called directly. It triggers
21536      * a toolbar update by reading the markup state of the current selection in the editor.
21537      */
21538     updateToolbar: function(){
21539
21540         if(!this.editorcore.activated){
21541             this.editor.onFirstFocus(); // is this neeed?
21542             return;
21543         }
21544
21545         var btns = this.buttons; 
21546         var doc = this.editorcore.doc;
21547         btns.get('bold').setActive(doc.queryCommandState('bold'));
21548         btns.get('italic').setActive(doc.queryCommandState('italic'));
21549         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21550         
21551         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21552         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21553         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21554         
21555         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21556         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21557          /*
21558         
21559         var ans = this.editorcore.getAllAncestors();
21560         if (this.formatCombo) {
21561             
21562             
21563             var store = this.formatCombo.store;
21564             this.formatCombo.setValue("");
21565             for (var i =0; i < ans.length;i++) {
21566                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21567                     // select it..
21568                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21569                     break;
21570                 }
21571             }
21572         }
21573         
21574         
21575         
21576         // hides menus... - so this cant be on a menu...
21577         Roo.bootstrap.MenuMgr.hideAll();
21578         */
21579         Roo.bootstrap.MenuMgr.hideAll();
21580         //this.editorsyncValue();
21581     },
21582     onFirstFocus: function() {
21583         this.buttons.each(function(item){
21584            item.enable();
21585         });
21586     },
21587     toggleSourceEdit : function(sourceEditMode){
21588         
21589           
21590         if(sourceEditMode){
21591             Roo.log("disabling buttons");
21592            this.buttons.each( function(item){
21593                 if(item.cmd != 'pencil'){
21594                     item.disable();
21595                 }
21596             });
21597           
21598         }else{
21599             Roo.log("enabling buttons");
21600             if(this.editorcore.initialized){
21601                 this.buttons.each( function(item){
21602                     item.enable();
21603                 });
21604             }
21605             
21606         }
21607         Roo.log("calling toggole on editor");
21608         // tell the editor that it's been pressed..
21609         this.editor.toggleSourceEdit(sourceEditMode);
21610        
21611     }
21612 });
21613
21614
21615
21616
21617
21618 /**
21619  * @class Roo.bootstrap.Table.AbstractSelectionModel
21620  * @extends Roo.util.Observable
21621  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21622  * implemented by descendant classes.  This class should not be directly instantiated.
21623  * @constructor
21624  */
21625 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21626     this.locked = false;
21627     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21628 };
21629
21630
21631 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21632     /** @ignore Called by the grid automatically. Do not call directly. */
21633     init : function(grid){
21634         this.grid = grid;
21635         this.initEvents();
21636     },
21637
21638     /**
21639      * Locks the selections.
21640      */
21641     lock : function(){
21642         this.locked = true;
21643     },
21644
21645     /**
21646      * Unlocks the selections.
21647      */
21648     unlock : function(){
21649         this.locked = false;
21650     },
21651
21652     /**
21653      * Returns true if the selections are locked.
21654      * @return {Boolean}
21655      */
21656     isLocked : function(){
21657         return this.locked;
21658     }
21659 });
21660 /**
21661  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21662  * @class Roo.bootstrap.Table.RowSelectionModel
21663  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21664  * It supports multiple selections and keyboard selection/navigation. 
21665  * @constructor
21666  * @param {Object} config
21667  */
21668
21669 Roo.bootstrap.Table.RowSelectionModel = function(config){
21670     Roo.apply(this, config);
21671     this.selections = new Roo.util.MixedCollection(false, function(o){
21672         return o.id;
21673     });
21674
21675     this.last = false;
21676     this.lastActive = false;
21677
21678     this.addEvents({
21679         /**
21680              * @event selectionchange
21681              * Fires when the selection changes
21682              * @param {SelectionModel} this
21683              */
21684             "selectionchange" : true,
21685         /**
21686              * @event afterselectionchange
21687              * Fires after the selection changes (eg. by key press or clicking)
21688              * @param {SelectionModel} this
21689              */
21690             "afterselectionchange" : true,
21691         /**
21692              * @event beforerowselect
21693              * Fires when a row is selected being selected, return false to cancel.
21694              * @param {SelectionModel} this
21695              * @param {Number} rowIndex The selected index
21696              * @param {Boolean} keepExisting False if other selections will be cleared
21697              */
21698             "beforerowselect" : true,
21699         /**
21700              * @event rowselect
21701              * Fires when a row is selected.
21702              * @param {SelectionModel} this
21703              * @param {Number} rowIndex The selected index
21704              * @param {Roo.data.Record} r The record
21705              */
21706             "rowselect" : true,
21707         /**
21708              * @event rowdeselect
21709              * Fires when a row is deselected.
21710              * @param {SelectionModel} this
21711              * @param {Number} rowIndex The selected index
21712              */
21713         "rowdeselect" : true
21714     });
21715     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21716     this.locked = false;
21717 };
21718
21719 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21720     /**
21721      * @cfg {Boolean} singleSelect
21722      * True to allow selection of only one row at a time (defaults to false)
21723      */
21724     singleSelect : false,
21725
21726     // private
21727     initEvents : function(){
21728
21729         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21730             this.grid.on("mousedown", this.handleMouseDown, this);
21731         }else{ // allow click to work like normal
21732             this.grid.on("rowclick", this.handleDragableRowClick, this);
21733         }
21734
21735         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21736             "up" : function(e){
21737                 if(!e.shiftKey){
21738                     this.selectPrevious(e.shiftKey);
21739                 }else if(this.last !== false && this.lastActive !== false){
21740                     var last = this.last;
21741                     this.selectRange(this.last,  this.lastActive-1);
21742                     this.grid.getView().focusRow(this.lastActive);
21743                     if(last !== false){
21744                         this.last = last;
21745                     }
21746                 }else{
21747                     this.selectFirstRow();
21748                 }
21749                 this.fireEvent("afterselectionchange", this);
21750             },
21751             "down" : function(e){
21752                 if(!e.shiftKey){
21753                     this.selectNext(e.shiftKey);
21754                 }else if(this.last !== false && this.lastActive !== false){
21755                     var last = this.last;
21756                     this.selectRange(this.last,  this.lastActive+1);
21757                     this.grid.getView().focusRow(this.lastActive);
21758                     if(last !== false){
21759                         this.last = last;
21760                     }
21761                 }else{
21762                     this.selectFirstRow();
21763                 }
21764                 this.fireEvent("afterselectionchange", this);
21765             },
21766             scope: this
21767         });
21768
21769         var view = this.grid.view;
21770         view.on("refresh", this.onRefresh, this);
21771         view.on("rowupdated", this.onRowUpdated, this);
21772         view.on("rowremoved", this.onRemove, this);
21773     },
21774
21775     // private
21776     onRefresh : function(){
21777         var ds = this.grid.dataSource, i, v = this.grid.view;
21778         var s = this.selections;
21779         s.each(function(r){
21780             if((i = ds.indexOfId(r.id)) != -1){
21781                 v.onRowSelect(i);
21782             }else{
21783                 s.remove(r);
21784             }
21785         });
21786     },
21787
21788     // private
21789     onRemove : function(v, index, r){
21790         this.selections.remove(r);
21791     },
21792
21793     // private
21794     onRowUpdated : function(v, index, r){
21795         if(this.isSelected(r)){
21796             v.onRowSelect(index);
21797         }
21798     },
21799
21800     /**
21801      * Select records.
21802      * @param {Array} records The records to select
21803      * @param {Boolean} keepExisting (optional) True to keep existing selections
21804      */
21805     selectRecords : function(records, keepExisting){
21806         if(!keepExisting){
21807             this.clearSelections();
21808         }
21809         var ds = this.grid.dataSource;
21810         for(var i = 0, len = records.length; i < len; i++){
21811             this.selectRow(ds.indexOf(records[i]), true);
21812         }
21813     },
21814
21815     /**
21816      * Gets the number of selected rows.
21817      * @return {Number}
21818      */
21819     getCount : function(){
21820         return this.selections.length;
21821     },
21822
21823     /**
21824      * Selects the first row in the grid.
21825      */
21826     selectFirstRow : function(){
21827         this.selectRow(0);
21828     },
21829
21830     /**
21831      * Select the last row.
21832      * @param {Boolean} keepExisting (optional) True to keep existing selections
21833      */
21834     selectLastRow : function(keepExisting){
21835         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21836     },
21837
21838     /**
21839      * Selects the row immediately following the last selected row.
21840      * @param {Boolean} keepExisting (optional) True to keep existing selections
21841      */
21842     selectNext : function(keepExisting){
21843         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21844             this.selectRow(this.last+1, keepExisting);
21845             this.grid.getView().focusRow(this.last);
21846         }
21847     },
21848
21849     /**
21850      * Selects the row that precedes the last selected row.
21851      * @param {Boolean} keepExisting (optional) True to keep existing selections
21852      */
21853     selectPrevious : function(keepExisting){
21854         if(this.last){
21855             this.selectRow(this.last-1, keepExisting);
21856             this.grid.getView().focusRow(this.last);
21857         }
21858     },
21859
21860     /**
21861      * Returns the selected records
21862      * @return {Array} Array of selected records
21863      */
21864     getSelections : function(){
21865         return [].concat(this.selections.items);
21866     },
21867
21868     /**
21869      * Returns the first selected record.
21870      * @return {Record}
21871      */
21872     getSelected : function(){
21873         return this.selections.itemAt(0);
21874     },
21875
21876
21877     /**
21878      * Clears all selections.
21879      */
21880     clearSelections : function(fast){
21881         if(this.locked) {
21882             return;
21883         }
21884         if(fast !== true){
21885             var ds = this.grid.dataSource;
21886             var s = this.selections;
21887             s.each(function(r){
21888                 this.deselectRow(ds.indexOfId(r.id));
21889             }, this);
21890             s.clear();
21891         }else{
21892             this.selections.clear();
21893         }
21894         this.last = false;
21895     },
21896
21897
21898     /**
21899      * Selects all rows.
21900      */
21901     selectAll : function(){
21902         if(this.locked) {
21903             return;
21904         }
21905         this.selections.clear();
21906         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21907             this.selectRow(i, true);
21908         }
21909     },
21910
21911     /**
21912      * Returns True if there is a selection.
21913      * @return {Boolean}
21914      */
21915     hasSelection : function(){
21916         return this.selections.length > 0;
21917     },
21918
21919     /**
21920      * Returns True if the specified row is selected.
21921      * @param {Number/Record} record The record or index of the record to check
21922      * @return {Boolean}
21923      */
21924     isSelected : function(index){
21925         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21926         return (r && this.selections.key(r.id) ? true : false);
21927     },
21928
21929     /**
21930      * Returns True if the specified record id is selected.
21931      * @param {String} id The id of record to check
21932      * @return {Boolean}
21933      */
21934     isIdSelected : function(id){
21935         return (this.selections.key(id) ? true : false);
21936     },
21937
21938     // private
21939     handleMouseDown : function(e, t){
21940         var view = this.grid.getView(), rowIndex;
21941         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21942             return;
21943         };
21944         if(e.shiftKey && this.last !== false){
21945             var last = this.last;
21946             this.selectRange(last, rowIndex, e.ctrlKey);
21947             this.last = last; // reset the last
21948             view.focusRow(rowIndex);
21949         }else{
21950             var isSelected = this.isSelected(rowIndex);
21951             if(e.button !== 0 && isSelected){
21952                 view.focusRow(rowIndex);
21953             }else if(e.ctrlKey && isSelected){
21954                 this.deselectRow(rowIndex);
21955             }else if(!isSelected){
21956                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21957                 view.focusRow(rowIndex);
21958             }
21959         }
21960         this.fireEvent("afterselectionchange", this);
21961     },
21962     // private
21963     handleDragableRowClick :  function(grid, rowIndex, e) 
21964     {
21965         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21966             this.selectRow(rowIndex, false);
21967             grid.view.focusRow(rowIndex);
21968              this.fireEvent("afterselectionchange", this);
21969         }
21970     },
21971     
21972     /**
21973      * Selects multiple rows.
21974      * @param {Array} rows Array of the indexes of the row to select
21975      * @param {Boolean} keepExisting (optional) True to keep existing selections
21976      */
21977     selectRows : function(rows, keepExisting){
21978         if(!keepExisting){
21979             this.clearSelections();
21980         }
21981         for(var i = 0, len = rows.length; i < len; i++){
21982             this.selectRow(rows[i], true);
21983         }
21984     },
21985
21986     /**
21987      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21988      * @param {Number} startRow The index of the first row in the range
21989      * @param {Number} endRow The index of the last row in the range
21990      * @param {Boolean} keepExisting (optional) True to retain existing selections
21991      */
21992     selectRange : function(startRow, endRow, keepExisting){
21993         if(this.locked) {
21994             return;
21995         }
21996         if(!keepExisting){
21997             this.clearSelections();
21998         }
21999         if(startRow <= endRow){
22000             for(var i = startRow; i <= endRow; i++){
22001                 this.selectRow(i, true);
22002             }
22003         }else{
22004             for(var i = startRow; i >= endRow; i--){
22005                 this.selectRow(i, true);
22006             }
22007         }
22008     },
22009
22010     /**
22011      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22012      * @param {Number} startRow The index of the first row in the range
22013      * @param {Number} endRow The index of the last row in the range
22014      */
22015     deselectRange : function(startRow, endRow, preventViewNotify){
22016         if(this.locked) {
22017             return;
22018         }
22019         for(var i = startRow; i <= endRow; i++){
22020             this.deselectRow(i, preventViewNotify);
22021         }
22022     },
22023
22024     /**
22025      * Selects a row.
22026      * @param {Number} row The index of the row to select
22027      * @param {Boolean} keepExisting (optional) True to keep existing selections
22028      */
22029     selectRow : function(index, keepExisting, preventViewNotify){
22030         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22031             return;
22032         }
22033         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22034             if(!keepExisting || this.singleSelect){
22035                 this.clearSelections();
22036             }
22037             var r = this.grid.dataSource.getAt(index);
22038             this.selections.add(r);
22039             this.last = this.lastActive = index;
22040             if(!preventViewNotify){
22041                 this.grid.getView().onRowSelect(index);
22042             }
22043             this.fireEvent("rowselect", this, index, r);
22044             this.fireEvent("selectionchange", this);
22045         }
22046     },
22047
22048     /**
22049      * Deselects a row.
22050      * @param {Number} row The index of the row to deselect
22051      */
22052     deselectRow : function(index, preventViewNotify){
22053         if(this.locked) {
22054             return;
22055         }
22056         if(this.last == index){
22057             this.last = false;
22058         }
22059         if(this.lastActive == index){
22060             this.lastActive = false;
22061         }
22062         var r = this.grid.dataSource.getAt(index);
22063         this.selections.remove(r);
22064         if(!preventViewNotify){
22065             this.grid.getView().onRowDeselect(index);
22066         }
22067         this.fireEvent("rowdeselect", this, index);
22068         this.fireEvent("selectionchange", this);
22069     },
22070
22071     // private
22072     restoreLast : function(){
22073         if(this._last){
22074             this.last = this._last;
22075         }
22076     },
22077
22078     // private
22079     acceptsNav : function(row, col, cm){
22080         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22081     },
22082
22083     // private
22084     onEditorKey : function(field, e){
22085         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22086         if(k == e.TAB){
22087             e.stopEvent();
22088             ed.completeEdit();
22089             if(e.shiftKey){
22090                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22091             }else{
22092                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22093             }
22094         }else if(k == e.ENTER && !e.ctrlKey){
22095             e.stopEvent();
22096             ed.completeEdit();
22097             if(e.shiftKey){
22098                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22099             }else{
22100                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22101             }
22102         }else if(k == e.ESC){
22103             ed.cancelEdit();
22104         }
22105         if(newCell){
22106             g.startEditing(newCell[0], newCell[1]);
22107         }
22108     }
22109 });/*
22110  * Based on:
22111  * Ext JS Library 1.1.1
22112  * Copyright(c) 2006-2007, Ext JS, LLC.
22113  *
22114  * Originally Released Under LGPL - original licence link has changed is not relivant.
22115  *
22116  * Fork - LGPL
22117  * <script type="text/javascript">
22118  */
22119  
22120 /**
22121  * @class Roo.bootstrap.PagingToolbar
22122  * @extends Roo.bootstrap.NavSimplebar
22123  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22124  * @constructor
22125  * Create a new PagingToolbar
22126  * @param {Object} config The config object
22127  * @param {Roo.data.Store} store
22128  */
22129 Roo.bootstrap.PagingToolbar = function(config)
22130 {
22131     // old args format still supported... - xtype is prefered..
22132         // created from xtype...
22133     
22134     this.ds = config.dataSource;
22135     
22136     if (config.store && !this.ds) {
22137         this.store= Roo.factory(config.store, Roo.data);
22138         this.ds = this.store;
22139         this.ds.xmodule = this.xmodule || false;
22140     }
22141     
22142     this.toolbarItems = [];
22143     if (config.items) {
22144         this.toolbarItems = config.items;
22145     }
22146     
22147     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22148     
22149     this.cursor = 0;
22150     
22151     if (this.ds) { 
22152         this.bind(this.ds);
22153     }
22154     
22155     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22156     
22157 };
22158
22159 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22160     /**
22161      * @cfg {Roo.data.Store} dataSource
22162      * The underlying data store providing the paged data
22163      */
22164     /**
22165      * @cfg {String/HTMLElement/Element} container
22166      * container The id or element that will contain the toolbar
22167      */
22168     /**
22169      * @cfg {Boolean} displayInfo
22170      * True to display the displayMsg (defaults to false)
22171      */
22172     /**
22173      * @cfg {Number} pageSize
22174      * The number of records to display per page (defaults to 20)
22175      */
22176     pageSize: 20,
22177     /**
22178      * @cfg {String} displayMsg
22179      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22180      */
22181     displayMsg : 'Displaying {0} - {1} of {2}',
22182     /**
22183      * @cfg {String} emptyMsg
22184      * The message to display when no records are found (defaults to "No data to display")
22185      */
22186     emptyMsg : 'No data to display',
22187     /**
22188      * Customizable piece of the default paging text (defaults to "Page")
22189      * @type String
22190      */
22191     beforePageText : "Page",
22192     /**
22193      * Customizable piece of the default paging text (defaults to "of %0")
22194      * @type String
22195      */
22196     afterPageText : "of {0}",
22197     /**
22198      * Customizable piece of the default paging text (defaults to "First Page")
22199      * @type String
22200      */
22201     firstText : "First Page",
22202     /**
22203      * Customizable piece of the default paging text (defaults to "Previous Page")
22204      * @type String
22205      */
22206     prevText : "Previous Page",
22207     /**
22208      * Customizable piece of the default paging text (defaults to "Next Page")
22209      * @type String
22210      */
22211     nextText : "Next Page",
22212     /**
22213      * Customizable piece of the default paging text (defaults to "Last Page")
22214      * @type String
22215      */
22216     lastText : "Last Page",
22217     /**
22218      * Customizable piece of the default paging text (defaults to "Refresh")
22219      * @type String
22220      */
22221     refreshText : "Refresh",
22222
22223     buttons : false,
22224     // private
22225     onRender : function(ct, position) 
22226     {
22227         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22228         this.navgroup.parentId = this.id;
22229         this.navgroup.onRender(this.el, null);
22230         // add the buttons to the navgroup
22231         
22232         if(this.displayInfo){
22233             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22234             this.displayEl = this.el.select('.x-paging-info', true).first();
22235 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22236 //            this.displayEl = navel.el.select('span',true).first();
22237         }
22238         
22239         var _this = this;
22240         
22241         if(this.buttons){
22242             Roo.each(_this.buttons, function(e){ // this might need to use render????
22243                Roo.factory(e).onRender(_this.el, null);
22244             });
22245         }
22246             
22247         Roo.each(_this.toolbarItems, function(e) {
22248             _this.navgroup.addItem(e);
22249         });
22250         
22251         
22252         this.first = this.navgroup.addItem({
22253             tooltip: this.firstText,
22254             cls: "prev",
22255             icon : 'fa fa-backward',
22256             disabled: true,
22257             preventDefault: true,
22258             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22259         });
22260         
22261         this.prev =  this.navgroup.addItem({
22262             tooltip: this.prevText,
22263             cls: "prev",
22264             icon : 'fa fa-step-backward',
22265             disabled: true,
22266             preventDefault: true,
22267             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22268         });
22269     //this.addSeparator();
22270         
22271         
22272         var field = this.navgroup.addItem( {
22273             tagtype : 'span',
22274             cls : 'x-paging-position',
22275             
22276             html : this.beforePageText  +
22277                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22278                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22279          } ); //?? escaped?
22280         
22281         this.field = field.el.select('input', true).first();
22282         this.field.on("keydown", this.onPagingKeydown, this);
22283         this.field.on("focus", function(){this.dom.select();});
22284     
22285     
22286         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22287         //this.field.setHeight(18);
22288         //this.addSeparator();
22289         this.next = this.navgroup.addItem({
22290             tooltip: this.nextText,
22291             cls: "next",
22292             html : ' <i class="fa fa-step-forward">',
22293             disabled: true,
22294             preventDefault: true,
22295             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22296         });
22297         this.last = this.navgroup.addItem({
22298             tooltip: this.lastText,
22299             icon : 'fa fa-forward',
22300             cls: "next",
22301             disabled: true,
22302             preventDefault: true,
22303             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22304         });
22305     //this.addSeparator();
22306         this.loading = this.navgroup.addItem({
22307             tooltip: this.refreshText,
22308             icon: 'fa fa-refresh',
22309             preventDefault: true,
22310             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22311         });
22312         
22313     },
22314
22315     // private
22316     updateInfo : function(){
22317         if(this.displayEl){
22318             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22319             var msg = count == 0 ?
22320                 this.emptyMsg :
22321                 String.format(
22322                     this.displayMsg,
22323                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22324                 );
22325             this.displayEl.update(msg);
22326         }
22327     },
22328
22329     // private
22330     onLoad : function(ds, r, o){
22331        this.cursor = o.params ? o.params.start : 0;
22332        var d = this.getPageData(),
22333             ap = d.activePage,
22334             ps = d.pages;
22335         
22336        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22337        this.field.dom.value = ap;
22338        this.first.setDisabled(ap == 1);
22339        this.prev.setDisabled(ap == 1);
22340        this.next.setDisabled(ap == ps);
22341        this.last.setDisabled(ap == ps);
22342        this.loading.enable();
22343        this.updateInfo();
22344     },
22345
22346     // private
22347     getPageData : function(){
22348         var total = this.ds.getTotalCount();
22349         return {
22350             total : total,
22351             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22352             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22353         };
22354     },
22355
22356     // private
22357     onLoadError : function(){
22358         this.loading.enable();
22359     },
22360
22361     // private
22362     onPagingKeydown : function(e){
22363         var k = e.getKey();
22364         var d = this.getPageData();
22365         if(k == e.RETURN){
22366             var v = this.field.dom.value, pageNum;
22367             if(!v || isNaN(pageNum = parseInt(v, 10))){
22368                 this.field.dom.value = d.activePage;
22369                 return;
22370             }
22371             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22372             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22373             e.stopEvent();
22374         }
22375         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))
22376         {
22377           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22378           this.field.dom.value = pageNum;
22379           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22380           e.stopEvent();
22381         }
22382         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22383         {
22384           var v = this.field.dom.value, pageNum; 
22385           var increment = (e.shiftKey) ? 10 : 1;
22386           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22387                 increment *= -1;
22388           }
22389           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22390             this.field.dom.value = d.activePage;
22391             return;
22392           }
22393           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22394           {
22395             this.field.dom.value = parseInt(v, 10) + increment;
22396             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22397             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22398           }
22399           e.stopEvent();
22400         }
22401     },
22402
22403     // private
22404     beforeLoad : function(){
22405         if(this.loading){
22406             this.loading.disable();
22407         }
22408     },
22409
22410     // private
22411     onClick : function(which){
22412         
22413         var ds = this.ds;
22414         if (!ds) {
22415             return;
22416         }
22417         
22418         switch(which){
22419             case "first":
22420                 ds.load({params:{start: 0, limit: this.pageSize}});
22421             break;
22422             case "prev":
22423                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22424             break;
22425             case "next":
22426                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22427             break;
22428             case "last":
22429                 var total = ds.getTotalCount();
22430                 var extra = total % this.pageSize;
22431                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22432                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22433             break;
22434             case "refresh":
22435                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22436             break;
22437         }
22438     },
22439
22440     /**
22441      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22442      * @param {Roo.data.Store} store The data store to unbind
22443      */
22444     unbind : function(ds){
22445         ds.un("beforeload", this.beforeLoad, this);
22446         ds.un("load", this.onLoad, this);
22447         ds.un("loadexception", this.onLoadError, this);
22448         ds.un("remove", this.updateInfo, this);
22449         ds.un("add", this.updateInfo, this);
22450         this.ds = undefined;
22451     },
22452
22453     /**
22454      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22455      * @param {Roo.data.Store} store The data store to bind
22456      */
22457     bind : function(ds){
22458         ds.on("beforeload", this.beforeLoad, this);
22459         ds.on("load", this.onLoad, this);
22460         ds.on("loadexception", this.onLoadError, this);
22461         ds.on("remove", this.updateInfo, this);
22462         ds.on("add", this.updateInfo, this);
22463         this.ds = ds;
22464     }
22465 });/*
22466  * - LGPL
22467  *
22468  * element
22469  * 
22470  */
22471
22472 /**
22473  * @class Roo.bootstrap.MessageBar
22474  * @extends Roo.bootstrap.Component
22475  * Bootstrap MessageBar class
22476  * @cfg {String} html contents of the MessageBar
22477  * @cfg {String} weight (info | success | warning | danger) default info
22478  * @cfg {String} beforeClass insert the bar before the given class
22479  * @cfg {Boolean} closable (true | false) default false
22480  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22481  * 
22482  * @constructor
22483  * Create a new Element
22484  * @param {Object} config The config object
22485  */
22486
22487 Roo.bootstrap.MessageBar = function(config){
22488     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22489 };
22490
22491 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22492     
22493     html: '',
22494     weight: 'info',
22495     closable: false,
22496     fixed: false,
22497     beforeClass: 'bootstrap-sticky-wrap',
22498     
22499     getAutoCreate : function(){
22500         
22501         var cfg = {
22502             tag: 'div',
22503             cls: 'alert alert-dismissable alert-' + this.weight,
22504             cn: [
22505                 {
22506                     tag: 'span',
22507                     cls: 'message',
22508                     html: this.html || ''
22509                 }
22510             ]
22511         };
22512         
22513         if(this.fixed){
22514             cfg.cls += ' alert-messages-fixed';
22515         }
22516         
22517         if(this.closable){
22518             cfg.cn.push({
22519                 tag: 'button',
22520                 cls: 'close',
22521                 html: 'x'
22522             });
22523         }
22524         
22525         return cfg;
22526     },
22527     
22528     onRender : function(ct, position)
22529     {
22530         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22531         
22532         if(!this.el){
22533             var cfg = Roo.apply({},  this.getAutoCreate());
22534             cfg.id = Roo.id();
22535             
22536             if (this.cls) {
22537                 cfg.cls += ' ' + this.cls;
22538             }
22539             if (this.style) {
22540                 cfg.style = this.style;
22541             }
22542             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22543             
22544             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22545         }
22546         
22547         this.el.select('>button.close').on('click', this.hide, this);
22548         
22549     },
22550     
22551     show : function()
22552     {
22553         if (!this.rendered) {
22554             this.render();
22555         }
22556         
22557         this.el.show();
22558         
22559         this.fireEvent('show', this);
22560         
22561     },
22562     
22563     hide : function()
22564     {
22565         if (!this.rendered) {
22566             this.render();
22567         }
22568         
22569         this.el.hide();
22570         
22571         this.fireEvent('hide', this);
22572     },
22573     
22574     update : function()
22575     {
22576 //        var e = this.el.dom.firstChild;
22577 //        
22578 //        if(this.closable){
22579 //            e = e.nextSibling;
22580 //        }
22581 //        
22582 //        e.data = this.html || '';
22583
22584         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22585     }
22586    
22587 });
22588
22589  
22590
22591      /*
22592  * - LGPL
22593  *
22594  * Graph
22595  * 
22596  */
22597
22598
22599 /**
22600  * @class Roo.bootstrap.Graph
22601  * @extends Roo.bootstrap.Component
22602  * Bootstrap Graph class
22603 > Prameters
22604  -sm {number} sm 4
22605  -md {number} md 5
22606  @cfg {String} graphtype  bar | vbar | pie
22607  @cfg {number} g_x coodinator | centre x (pie)
22608  @cfg {number} g_y coodinator | centre y (pie)
22609  @cfg {number} g_r radius (pie)
22610  @cfg {number} g_height height of the chart (respected by all elements in the set)
22611  @cfg {number} g_width width of the chart (respected by all elements in the set)
22612  @cfg {Object} title The title of the chart
22613     
22614  -{Array}  values
22615  -opts (object) options for the chart 
22616      o {
22617      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22618      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22619      o vgutter (number)
22620      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.
22621      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22622      o to
22623      o stretch (boolean)
22624      o }
22625  -opts (object) options for the pie
22626      o{
22627      o cut
22628      o startAngle (number)
22629      o endAngle (number)
22630      } 
22631  *
22632  * @constructor
22633  * Create a new Input
22634  * @param {Object} config The config object
22635  */
22636
22637 Roo.bootstrap.Graph = function(config){
22638     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22639     
22640     this.addEvents({
22641         // img events
22642         /**
22643          * @event click
22644          * The img click event for the img.
22645          * @param {Roo.EventObject} e
22646          */
22647         "click" : true
22648     });
22649 };
22650
22651 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22652     
22653     sm: 4,
22654     md: 5,
22655     graphtype: 'bar',
22656     g_height: 250,
22657     g_width: 400,
22658     g_x: 50,
22659     g_y: 50,
22660     g_r: 30,
22661     opts:{
22662         //g_colors: this.colors,
22663         g_type: 'soft',
22664         g_gutter: '20%'
22665
22666     },
22667     title : false,
22668
22669     getAutoCreate : function(){
22670         
22671         var cfg = {
22672             tag: 'div',
22673             html : null
22674         };
22675         
22676         
22677         return  cfg;
22678     },
22679
22680     onRender : function(ct,position){
22681         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22682         this.raphael = Raphael(this.el.dom);
22683         
22684                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22685                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22686                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22687                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22688                 /*
22689                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22690                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22691                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22692                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22693                 
22694                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22695                 r.barchart(330, 10, 300, 220, data1);
22696                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22697                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22698                 */
22699                 
22700                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22701                 // r.barchart(30, 30, 560, 250,  xdata, {
22702                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22703                 //     axis : "0 0 1 1",
22704                 //     axisxlabels :  xdata
22705                 //     //yvalues : cols,
22706                    
22707                 // });
22708 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22709 //        
22710 //        this.load(null,xdata,{
22711 //                axis : "0 0 1 1",
22712 //                axisxlabels :  xdata
22713 //                });
22714
22715     },
22716
22717     load : function(graphtype,xdata,opts){
22718         this.raphael.clear();
22719         if(!graphtype) {
22720             graphtype = this.graphtype;
22721         }
22722         if(!opts){
22723             opts = this.opts;
22724         }
22725         var r = this.raphael,
22726             fin = function () {
22727                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22728             },
22729             fout = function () {
22730                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22731             },
22732             pfin = function() {
22733                 this.sector.stop();
22734                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22735
22736                 if (this.label) {
22737                     this.label[0].stop();
22738                     this.label[0].attr({ r: 7.5 });
22739                     this.label[1].attr({ "font-weight": 800 });
22740                 }
22741             },
22742             pfout = function() {
22743                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22744
22745                 if (this.label) {
22746                     this.label[0].animate({ r: 5 }, 500, "bounce");
22747                     this.label[1].attr({ "font-weight": 400 });
22748                 }
22749             };
22750
22751         switch(graphtype){
22752             case 'bar':
22753                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22754                 break;
22755             case 'hbar':
22756                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22757                 break;
22758             case 'pie':
22759 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22760 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22761 //            
22762                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22763                 
22764                 break;
22765
22766         }
22767         
22768         if(this.title){
22769             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22770         }
22771         
22772     },
22773     
22774     setTitle: function(o)
22775     {
22776         this.title = o;
22777     },
22778     
22779     initEvents: function() {
22780         
22781         if(!this.href){
22782             this.el.on('click', this.onClick, this);
22783         }
22784     },
22785     
22786     onClick : function(e)
22787     {
22788         Roo.log('img onclick');
22789         this.fireEvent('click', this, e);
22790     }
22791    
22792 });
22793
22794  
22795 /*
22796  * - LGPL
22797  *
22798  * numberBox
22799  * 
22800  */
22801 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22802
22803 /**
22804  * @class Roo.bootstrap.dash.NumberBox
22805  * @extends Roo.bootstrap.Component
22806  * Bootstrap NumberBox class
22807  * @cfg {String} headline Box headline
22808  * @cfg {String} content Box content
22809  * @cfg {String} icon Box icon
22810  * @cfg {String} footer Footer text
22811  * @cfg {String} fhref Footer href
22812  * 
22813  * @constructor
22814  * Create a new NumberBox
22815  * @param {Object} config The config object
22816  */
22817
22818
22819 Roo.bootstrap.dash.NumberBox = function(config){
22820     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22821     
22822 };
22823
22824 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22825     
22826     headline : '',
22827     content : '',
22828     icon : '',
22829     footer : '',
22830     fhref : '',
22831     ficon : '',
22832     
22833     getAutoCreate : function(){
22834         
22835         var cfg = {
22836             tag : 'div',
22837             cls : 'small-box ',
22838             cn : [
22839                 {
22840                     tag : 'div',
22841                     cls : 'inner',
22842                     cn :[
22843                         {
22844                             tag : 'h3',
22845                             cls : 'roo-headline',
22846                             html : this.headline
22847                         },
22848                         {
22849                             tag : 'p',
22850                             cls : 'roo-content',
22851                             html : this.content
22852                         }
22853                     ]
22854                 }
22855             ]
22856         };
22857         
22858         if(this.icon){
22859             cfg.cn.push({
22860                 tag : 'div',
22861                 cls : 'icon',
22862                 cn :[
22863                     {
22864                         tag : 'i',
22865                         cls : 'ion ' + this.icon
22866                     }
22867                 ]
22868             });
22869         }
22870         
22871         if(this.footer){
22872             var footer = {
22873                 tag : 'a',
22874                 cls : 'small-box-footer',
22875                 href : this.fhref || '#',
22876                 html : this.footer
22877             };
22878             
22879             cfg.cn.push(footer);
22880             
22881         }
22882         
22883         return  cfg;
22884     },
22885
22886     onRender : function(ct,position){
22887         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22888
22889
22890        
22891                 
22892     },
22893
22894     setHeadline: function (value)
22895     {
22896         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22897     },
22898     
22899     setFooter: function (value, href)
22900     {
22901         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22902         
22903         if(href){
22904             this.el.select('a.small-box-footer',true).first().attr('href', href);
22905         }
22906         
22907     },
22908
22909     setContent: function (value)
22910     {
22911         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22912     },
22913
22914     initEvents: function() 
22915     {   
22916         
22917     }
22918     
22919 });
22920
22921  
22922 /*
22923  * - LGPL
22924  *
22925  * TabBox
22926  * 
22927  */
22928 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22929
22930 /**
22931  * @class Roo.bootstrap.dash.TabBox
22932  * @extends Roo.bootstrap.Component
22933  * Bootstrap TabBox class
22934  * @cfg {String} title Title of the TabBox
22935  * @cfg {String} icon Icon of the TabBox
22936  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22937  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22938  * 
22939  * @constructor
22940  * Create a new TabBox
22941  * @param {Object} config The config object
22942  */
22943
22944
22945 Roo.bootstrap.dash.TabBox = function(config){
22946     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22947     this.addEvents({
22948         // raw events
22949         /**
22950          * @event addpane
22951          * When a pane is added
22952          * @param {Roo.bootstrap.dash.TabPane} pane
22953          */
22954         "addpane" : true,
22955         /**
22956          * @event activatepane
22957          * When a pane is activated
22958          * @param {Roo.bootstrap.dash.TabPane} pane
22959          */
22960         "activatepane" : true
22961         
22962          
22963     });
22964     
22965     this.panes = [];
22966 };
22967
22968 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22969
22970     title : '',
22971     icon : false,
22972     showtabs : true,
22973     tabScrollable : false,
22974     
22975     getChildContainer : function()
22976     {
22977         return this.el.select('.tab-content', true).first();
22978     },
22979     
22980     getAutoCreate : function(){
22981         
22982         var header = {
22983             tag: 'li',
22984             cls: 'pull-left header',
22985             html: this.title,
22986             cn : []
22987         };
22988         
22989         if(this.icon){
22990             header.cn.push({
22991                 tag: 'i',
22992                 cls: 'fa ' + this.icon
22993             });
22994         }
22995         
22996         var h = {
22997             tag: 'ul',
22998             cls: 'nav nav-tabs pull-right',
22999             cn: [
23000                 header
23001             ]
23002         };
23003         
23004         if(this.tabScrollable){
23005             h = {
23006                 tag: 'div',
23007                 cls: 'tab-header',
23008                 cn: [
23009                     {
23010                         tag: 'ul',
23011                         cls: 'nav nav-tabs pull-right',
23012                         cn: [
23013                             header
23014                         ]
23015                     }
23016                 ]
23017             };
23018         }
23019         
23020         var cfg = {
23021             tag: 'div',
23022             cls: 'nav-tabs-custom',
23023             cn: [
23024                 h,
23025                 {
23026                     tag: 'div',
23027                     cls: 'tab-content no-padding',
23028                     cn: []
23029                 }
23030             ]
23031         };
23032
23033         return  cfg;
23034     },
23035     initEvents : function()
23036     {
23037         //Roo.log('add add pane handler');
23038         this.on('addpane', this.onAddPane, this);
23039     },
23040      /**
23041      * Updates the box title
23042      * @param {String} html to set the title to.
23043      */
23044     setTitle : function(value)
23045     {
23046         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23047     },
23048     onAddPane : function(pane)
23049     {
23050         this.panes.push(pane);
23051         //Roo.log('addpane');
23052         //Roo.log(pane);
23053         // tabs are rendere left to right..
23054         if(!this.showtabs){
23055             return;
23056         }
23057         
23058         var ctr = this.el.select('.nav-tabs', true).first();
23059          
23060          
23061         var existing = ctr.select('.nav-tab',true);
23062         var qty = existing.getCount();;
23063         
23064         
23065         var tab = ctr.createChild({
23066             tag : 'li',
23067             cls : 'nav-tab' + (qty ? '' : ' active'),
23068             cn : [
23069                 {
23070                     tag : 'a',
23071                     href:'#',
23072                     html : pane.title
23073                 }
23074             ]
23075         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23076         pane.tab = tab;
23077         
23078         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23079         if (!qty) {
23080             pane.el.addClass('active');
23081         }
23082         
23083                 
23084     },
23085     onTabClick : function(ev,un,ob,pane)
23086     {
23087         //Roo.log('tab - prev default');
23088         ev.preventDefault();
23089         
23090         
23091         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23092         pane.tab.addClass('active');
23093         //Roo.log(pane.title);
23094         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23095         // technically we should have a deactivate event.. but maybe add later.
23096         // and it should not de-activate the selected tab...
23097         this.fireEvent('activatepane', pane);
23098         pane.el.addClass('active');
23099         pane.fireEvent('activate');
23100         
23101         
23102     },
23103     
23104     getActivePane : function()
23105     {
23106         var r = false;
23107         Roo.each(this.panes, function(p) {
23108             if(p.el.hasClass('active')){
23109                 r = p;
23110                 return false;
23111             }
23112             
23113             return;
23114         });
23115         
23116         return r;
23117     }
23118     
23119     
23120 });
23121
23122  
23123 /*
23124  * - LGPL
23125  *
23126  * Tab pane
23127  * 
23128  */
23129 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23130 /**
23131  * @class Roo.bootstrap.TabPane
23132  * @extends Roo.bootstrap.Component
23133  * Bootstrap TabPane class
23134  * @cfg {Boolean} active (false | true) Default false
23135  * @cfg {String} title title of panel
23136
23137  * 
23138  * @constructor
23139  * Create a new TabPane
23140  * @param {Object} config The config object
23141  */
23142
23143 Roo.bootstrap.dash.TabPane = function(config){
23144     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23145     
23146     this.addEvents({
23147         // raw events
23148         /**
23149          * @event activate
23150          * When a pane is activated
23151          * @param {Roo.bootstrap.dash.TabPane} pane
23152          */
23153         "activate" : true
23154          
23155     });
23156 };
23157
23158 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23159     
23160     active : false,
23161     title : '',
23162     
23163     // the tabBox that this is attached to.
23164     tab : false,
23165      
23166     getAutoCreate : function() 
23167     {
23168         var cfg = {
23169             tag: 'div',
23170             cls: 'tab-pane'
23171         };
23172         
23173         if(this.active){
23174             cfg.cls += ' active';
23175         }
23176         
23177         return cfg;
23178     },
23179     initEvents  : function()
23180     {
23181         //Roo.log('trigger add pane handler');
23182         this.parent().fireEvent('addpane', this)
23183     },
23184     
23185      /**
23186      * Updates the tab title 
23187      * @param {String} html to set the title to.
23188      */
23189     setTitle: function(str)
23190     {
23191         if (!this.tab) {
23192             return;
23193         }
23194         this.title = str;
23195         this.tab.select('a', true).first().dom.innerHTML = str;
23196         
23197     }
23198     
23199     
23200     
23201 });
23202
23203  
23204
23205
23206  /*
23207  * - LGPL
23208  *
23209  * menu
23210  * 
23211  */
23212 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23213
23214 /**
23215  * @class Roo.bootstrap.menu.Menu
23216  * @extends Roo.bootstrap.Component
23217  * Bootstrap Menu class - container for Menu
23218  * @cfg {String} html Text of the menu
23219  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23220  * @cfg {String} icon Font awesome icon
23221  * @cfg {String} pos Menu align to (top | bottom) default bottom
23222  * 
23223  * 
23224  * @constructor
23225  * Create a new Menu
23226  * @param {Object} config The config object
23227  */
23228
23229
23230 Roo.bootstrap.menu.Menu = function(config){
23231     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23232     
23233     this.addEvents({
23234         /**
23235          * @event beforeshow
23236          * Fires before this menu is displayed
23237          * @param {Roo.bootstrap.menu.Menu} this
23238          */
23239         beforeshow : true,
23240         /**
23241          * @event beforehide
23242          * Fires before this menu is hidden
23243          * @param {Roo.bootstrap.menu.Menu} this
23244          */
23245         beforehide : true,
23246         /**
23247          * @event show
23248          * Fires after this menu is displayed
23249          * @param {Roo.bootstrap.menu.Menu} this
23250          */
23251         show : true,
23252         /**
23253          * @event hide
23254          * Fires after this menu is hidden
23255          * @param {Roo.bootstrap.menu.Menu} this
23256          */
23257         hide : true,
23258         /**
23259          * @event click
23260          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23261          * @param {Roo.bootstrap.menu.Menu} this
23262          * @param {Roo.EventObject} e
23263          */
23264         click : true
23265     });
23266     
23267 };
23268
23269 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23270     
23271     submenu : false,
23272     html : '',
23273     weight : 'default',
23274     icon : false,
23275     pos : 'bottom',
23276     
23277     
23278     getChildContainer : function() {
23279         if(this.isSubMenu){
23280             return this.el;
23281         }
23282         
23283         return this.el.select('ul.dropdown-menu', true).first();  
23284     },
23285     
23286     getAutoCreate : function()
23287     {
23288         var text = [
23289             {
23290                 tag : 'span',
23291                 cls : 'roo-menu-text',
23292                 html : this.html
23293             }
23294         ];
23295         
23296         if(this.icon){
23297             text.unshift({
23298                 tag : 'i',
23299                 cls : 'fa ' + this.icon
23300             })
23301         }
23302         
23303         
23304         var cfg = {
23305             tag : 'div',
23306             cls : 'btn-group',
23307             cn : [
23308                 {
23309                     tag : 'button',
23310                     cls : 'dropdown-button btn btn-' + this.weight,
23311                     cn : text
23312                 },
23313                 {
23314                     tag : 'button',
23315                     cls : 'dropdown-toggle btn btn-' + this.weight,
23316                     cn : [
23317                         {
23318                             tag : 'span',
23319                             cls : 'caret'
23320                         }
23321                     ]
23322                 },
23323                 {
23324                     tag : 'ul',
23325                     cls : 'dropdown-menu'
23326                 }
23327             ]
23328             
23329         };
23330         
23331         if(this.pos == 'top'){
23332             cfg.cls += ' dropup';
23333         }
23334         
23335         if(this.isSubMenu){
23336             cfg = {
23337                 tag : 'ul',
23338                 cls : 'dropdown-menu'
23339             }
23340         }
23341         
23342         return cfg;
23343     },
23344     
23345     onRender : function(ct, position)
23346     {
23347         this.isSubMenu = ct.hasClass('dropdown-submenu');
23348         
23349         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23350     },
23351     
23352     initEvents : function() 
23353     {
23354         if(this.isSubMenu){
23355             return;
23356         }
23357         
23358         this.hidden = true;
23359         
23360         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23361         this.triggerEl.on('click', this.onTriggerPress, this);
23362         
23363         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23364         this.buttonEl.on('click', this.onClick, this);
23365         
23366     },
23367     
23368     list : function()
23369     {
23370         if(this.isSubMenu){
23371             return this.el;
23372         }
23373         
23374         return this.el.select('ul.dropdown-menu', true).first();
23375     },
23376     
23377     onClick : function(e)
23378     {
23379         this.fireEvent("click", this, e);
23380     },
23381     
23382     onTriggerPress  : function(e)
23383     {   
23384         if (this.isVisible()) {
23385             this.hide();
23386         } else {
23387             this.show();
23388         }
23389     },
23390     
23391     isVisible : function(){
23392         return !this.hidden;
23393     },
23394     
23395     show : function()
23396     {
23397         this.fireEvent("beforeshow", this);
23398         
23399         this.hidden = false;
23400         this.el.addClass('open');
23401         
23402         Roo.get(document).on("mouseup", this.onMouseUp, this);
23403         
23404         this.fireEvent("show", this);
23405         
23406         
23407     },
23408     
23409     hide : function()
23410     {
23411         this.fireEvent("beforehide", this);
23412         
23413         this.hidden = true;
23414         this.el.removeClass('open');
23415         
23416         Roo.get(document).un("mouseup", this.onMouseUp);
23417         
23418         this.fireEvent("hide", this);
23419     },
23420     
23421     onMouseUp : function()
23422     {
23423         this.hide();
23424     }
23425     
23426 });
23427
23428  
23429  /*
23430  * - LGPL
23431  *
23432  * menu item
23433  * 
23434  */
23435 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23436
23437 /**
23438  * @class Roo.bootstrap.menu.Item
23439  * @extends Roo.bootstrap.Component
23440  * Bootstrap MenuItem class
23441  * @cfg {Boolean} submenu (true | false) default false
23442  * @cfg {String} html text of the item
23443  * @cfg {String} href the link
23444  * @cfg {Boolean} disable (true | false) default false
23445  * @cfg {Boolean} preventDefault (true | false) default true
23446  * @cfg {String} icon Font awesome icon
23447  * @cfg {String} pos Submenu align to (left | right) default right 
23448  * 
23449  * 
23450  * @constructor
23451  * Create a new Item
23452  * @param {Object} config The config object
23453  */
23454
23455
23456 Roo.bootstrap.menu.Item = function(config){
23457     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23458     this.addEvents({
23459         /**
23460          * @event mouseover
23461          * Fires when the mouse is hovering over this menu
23462          * @param {Roo.bootstrap.menu.Item} this
23463          * @param {Roo.EventObject} e
23464          */
23465         mouseover : true,
23466         /**
23467          * @event mouseout
23468          * Fires when the mouse exits this menu
23469          * @param {Roo.bootstrap.menu.Item} this
23470          * @param {Roo.EventObject} e
23471          */
23472         mouseout : true,
23473         // raw events
23474         /**
23475          * @event click
23476          * The raw click event for the entire grid.
23477          * @param {Roo.EventObject} e
23478          */
23479         click : true
23480     });
23481 };
23482
23483 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23484     
23485     submenu : false,
23486     href : '',
23487     html : '',
23488     preventDefault: true,
23489     disable : false,
23490     icon : false,
23491     pos : 'right',
23492     
23493     getAutoCreate : function()
23494     {
23495         var text = [
23496             {
23497                 tag : 'span',
23498                 cls : 'roo-menu-item-text',
23499                 html : this.html
23500             }
23501         ];
23502         
23503         if(this.icon){
23504             text.unshift({
23505                 tag : 'i',
23506                 cls : 'fa ' + this.icon
23507             })
23508         }
23509         
23510         var cfg = {
23511             tag : 'li',
23512             cn : [
23513                 {
23514                     tag : 'a',
23515                     href : this.href || '#',
23516                     cn : text
23517                 }
23518             ]
23519         };
23520         
23521         if(this.disable){
23522             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23523         }
23524         
23525         if(this.submenu){
23526             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23527             
23528             if(this.pos == 'left'){
23529                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23530             }
23531         }
23532         
23533         return cfg;
23534     },
23535     
23536     initEvents : function() 
23537     {
23538         this.el.on('mouseover', this.onMouseOver, this);
23539         this.el.on('mouseout', this.onMouseOut, this);
23540         
23541         this.el.select('a', true).first().on('click', this.onClick, this);
23542         
23543     },
23544     
23545     onClick : function(e)
23546     {
23547         if(this.preventDefault){
23548             e.preventDefault();
23549         }
23550         
23551         this.fireEvent("click", this, e);
23552     },
23553     
23554     onMouseOver : function(e)
23555     {
23556         if(this.submenu && this.pos == 'left'){
23557             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23558         }
23559         
23560         this.fireEvent("mouseover", this, e);
23561     },
23562     
23563     onMouseOut : function(e)
23564     {
23565         this.fireEvent("mouseout", this, e);
23566     }
23567 });
23568
23569  
23570
23571  /*
23572  * - LGPL
23573  *
23574  * menu separator
23575  * 
23576  */
23577 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23578
23579 /**
23580  * @class Roo.bootstrap.menu.Separator
23581  * @extends Roo.bootstrap.Component
23582  * Bootstrap Separator class
23583  * 
23584  * @constructor
23585  * Create a new Separator
23586  * @param {Object} config The config object
23587  */
23588
23589
23590 Roo.bootstrap.menu.Separator = function(config){
23591     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23592 };
23593
23594 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23595     
23596     getAutoCreate : function(){
23597         var cfg = {
23598             tag : 'li',
23599             cls: 'divider'
23600         };
23601         
23602         return cfg;
23603     }
23604    
23605 });
23606
23607  
23608
23609  /*
23610  * - LGPL
23611  *
23612  * Tooltip
23613  * 
23614  */
23615
23616 /**
23617  * @class Roo.bootstrap.Tooltip
23618  * Bootstrap Tooltip class
23619  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23620  * to determine which dom element triggers the tooltip.
23621  * 
23622  * It needs to add support for additional attributes like tooltip-position
23623  * 
23624  * @constructor
23625  * Create a new Toolti
23626  * @param {Object} config The config object
23627  */
23628
23629 Roo.bootstrap.Tooltip = function(config){
23630     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23631 };
23632
23633 Roo.apply(Roo.bootstrap.Tooltip, {
23634     /**
23635      * @function init initialize tooltip monitoring.
23636      * @static
23637      */
23638     currentEl : false,
23639     currentTip : false,
23640     currentRegion : false,
23641     
23642     //  init : delay?
23643     
23644     init : function()
23645     {
23646         Roo.get(document).on('mouseover', this.enter ,this);
23647         Roo.get(document).on('mouseout', this.leave, this);
23648          
23649         
23650         this.currentTip = new Roo.bootstrap.Tooltip();
23651     },
23652     
23653     enter : function(ev)
23654     {
23655         var dom = ev.getTarget();
23656         
23657         //Roo.log(['enter',dom]);
23658         var el = Roo.fly(dom);
23659         if (this.currentEl) {
23660             //Roo.log(dom);
23661             //Roo.log(this.currentEl);
23662             //Roo.log(this.currentEl.contains(dom));
23663             if (this.currentEl == el) {
23664                 return;
23665             }
23666             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23667                 return;
23668             }
23669
23670         }
23671         
23672         if (this.currentTip.el) {
23673             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23674         }    
23675         //Roo.log(ev);
23676         var bindEl = el;
23677         
23678         // you can not look for children, as if el is the body.. then everythign is the child..
23679         if (!el.attr('tooltip')) { //
23680             if (!el.select("[tooltip]").elements.length) {
23681                 return;
23682             }
23683             // is the mouse over this child...?
23684             bindEl = el.select("[tooltip]").first();
23685             var xy = ev.getXY();
23686             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23687                 //Roo.log("not in region.");
23688                 return;
23689             }
23690             //Roo.log("child element over..");
23691             
23692         }
23693         this.currentEl = bindEl;
23694         this.currentTip.bind(bindEl);
23695         this.currentRegion = Roo.lib.Region.getRegion(dom);
23696         this.currentTip.enter();
23697         
23698     },
23699     leave : function(ev)
23700     {
23701         var dom = ev.getTarget();
23702         //Roo.log(['leave',dom]);
23703         if (!this.currentEl) {
23704             return;
23705         }
23706         
23707         
23708         if (dom != this.currentEl.dom) {
23709             return;
23710         }
23711         var xy = ev.getXY();
23712         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23713             return;
23714         }
23715         // only activate leave if mouse cursor is outside... bounding box..
23716         
23717         
23718         
23719         
23720         if (this.currentTip) {
23721             this.currentTip.leave();
23722         }
23723         //Roo.log('clear currentEl');
23724         this.currentEl = false;
23725         
23726         
23727     },
23728     alignment : {
23729         'left' : ['r-l', [-2,0], 'right'],
23730         'right' : ['l-r', [2,0], 'left'],
23731         'bottom' : ['t-b', [0,2], 'top'],
23732         'top' : [ 'b-t', [0,-2], 'bottom']
23733     }
23734     
23735 });
23736
23737
23738 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23739     
23740     
23741     bindEl : false,
23742     
23743     delay : null, // can be { show : 300 , hide: 500}
23744     
23745     timeout : null,
23746     
23747     hoverState : null, //???
23748     
23749     placement : 'bottom', 
23750     
23751     getAutoCreate : function(){
23752     
23753         var cfg = {
23754            cls : 'tooltip',
23755            role : 'tooltip',
23756            cn : [
23757                 {
23758                     cls : 'tooltip-arrow'
23759                 },
23760                 {
23761                     cls : 'tooltip-inner'
23762                 }
23763            ]
23764         };
23765         
23766         return cfg;
23767     },
23768     bind : function(el)
23769     {
23770         this.bindEl = el;
23771     },
23772       
23773     
23774     enter : function () {
23775        
23776         if (this.timeout != null) {
23777             clearTimeout(this.timeout);
23778         }
23779         
23780         this.hoverState = 'in';
23781          //Roo.log("enter - show");
23782         if (!this.delay || !this.delay.show) {
23783             this.show();
23784             return;
23785         }
23786         var _t = this;
23787         this.timeout = setTimeout(function () {
23788             if (_t.hoverState == 'in') {
23789                 _t.show();
23790             }
23791         }, this.delay.show);
23792     },
23793     leave : function()
23794     {
23795         clearTimeout(this.timeout);
23796     
23797         this.hoverState = 'out';
23798          if (!this.delay || !this.delay.hide) {
23799             this.hide();
23800             return;
23801         }
23802        
23803         var _t = this;
23804         this.timeout = setTimeout(function () {
23805             //Roo.log("leave - timeout");
23806             
23807             if (_t.hoverState == 'out') {
23808                 _t.hide();
23809                 Roo.bootstrap.Tooltip.currentEl = false;
23810             }
23811         }, delay);
23812     },
23813     
23814     show : function ()
23815     {
23816         if (!this.el) {
23817             this.render(document.body);
23818         }
23819         // set content.
23820         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23821         
23822         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23823         
23824         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23825         
23826         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23827         
23828         var placement = typeof this.placement == 'function' ?
23829             this.placement.call(this, this.el, on_el) :
23830             this.placement;
23831             
23832         var autoToken = /\s?auto?\s?/i;
23833         var autoPlace = autoToken.test(placement);
23834         if (autoPlace) {
23835             placement = placement.replace(autoToken, '') || 'top';
23836         }
23837         
23838         //this.el.detach()
23839         //this.el.setXY([0,0]);
23840         this.el.show();
23841         //this.el.dom.style.display='block';
23842         
23843         //this.el.appendTo(on_el);
23844         
23845         var p = this.getPosition();
23846         var box = this.el.getBox();
23847         
23848         if (autoPlace) {
23849             // fixme..
23850         }
23851         
23852         var align = Roo.bootstrap.Tooltip.alignment[placement];
23853         
23854         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23855         
23856         if(placement == 'top' || placement == 'bottom'){
23857             if(xy[0] < 0){
23858                 placement = 'right';
23859             }
23860             
23861             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23862                 placement = 'left';
23863             }
23864         }
23865         
23866         align = Roo.bootstrap.Tooltip.alignment[placement];
23867         
23868         this.el.alignTo(this.bindEl, align[0],align[1]);
23869         //var arrow = this.el.select('.arrow',true).first();
23870         //arrow.set(align[2], 
23871         
23872         this.el.addClass(placement);
23873         
23874         this.el.addClass('in fade');
23875         
23876         this.hoverState = null;
23877         
23878         if (this.el.hasClass('fade')) {
23879             // fade it?
23880         }
23881         
23882     },
23883     hide : function()
23884     {
23885          
23886         if (!this.el) {
23887             return;
23888         }
23889         //this.el.setXY([0,0]);
23890         this.el.removeClass('in');
23891         //this.el.hide();
23892         
23893     }
23894     
23895 });
23896  
23897
23898  /*
23899  * - LGPL
23900  *
23901  * Location Picker
23902  * 
23903  */
23904
23905 /**
23906  * @class Roo.bootstrap.LocationPicker
23907  * @extends Roo.bootstrap.Component
23908  * Bootstrap LocationPicker class
23909  * @cfg {Number} latitude Position when init default 0
23910  * @cfg {Number} longitude Position when init default 0
23911  * @cfg {Number} zoom default 15
23912  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23913  * @cfg {Boolean} mapTypeControl default false
23914  * @cfg {Boolean} disableDoubleClickZoom default false
23915  * @cfg {Boolean} scrollwheel default true
23916  * @cfg {Boolean} streetViewControl default false
23917  * @cfg {Number} radius default 0
23918  * @cfg {String} locationName
23919  * @cfg {Boolean} draggable default true
23920  * @cfg {Boolean} enableAutocomplete default false
23921  * @cfg {Boolean} enableReverseGeocode default true
23922  * @cfg {String} markerTitle
23923  * 
23924  * @constructor
23925  * Create a new LocationPicker
23926  * @param {Object} config The config object
23927  */
23928
23929
23930 Roo.bootstrap.LocationPicker = function(config){
23931     
23932     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23933     
23934     this.addEvents({
23935         /**
23936          * @event initial
23937          * Fires when the picker initialized.
23938          * @param {Roo.bootstrap.LocationPicker} this
23939          * @param {Google Location} location
23940          */
23941         initial : true,
23942         /**
23943          * @event positionchanged
23944          * Fires when the picker position changed.
23945          * @param {Roo.bootstrap.LocationPicker} this
23946          * @param {Google Location} location
23947          */
23948         positionchanged : true,
23949         /**
23950          * @event resize
23951          * Fires when the map resize.
23952          * @param {Roo.bootstrap.LocationPicker} this
23953          */
23954         resize : true,
23955         /**
23956          * @event show
23957          * Fires when the map show.
23958          * @param {Roo.bootstrap.LocationPicker} this
23959          */
23960         show : true,
23961         /**
23962          * @event hide
23963          * Fires when the map hide.
23964          * @param {Roo.bootstrap.LocationPicker} this
23965          */
23966         hide : true,
23967         /**
23968          * @event mapClick
23969          * Fires when click the map.
23970          * @param {Roo.bootstrap.LocationPicker} this
23971          * @param {Map event} e
23972          */
23973         mapClick : true,
23974         /**
23975          * @event mapRightClick
23976          * Fires when right click the map.
23977          * @param {Roo.bootstrap.LocationPicker} this
23978          * @param {Map event} e
23979          */
23980         mapRightClick : true,
23981         /**
23982          * @event markerClick
23983          * Fires when click the marker.
23984          * @param {Roo.bootstrap.LocationPicker} this
23985          * @param {Map event} e
23986          */
23987         markerClick : true,
23988         /**
23989          * @event markerRightClick
23990          * Fires when right click the marker.
23991          * @param {Roo.bootstrap.LocationPicker} this
23992          * @param {Map event} e
23993          */
23994         markerRightClick : true,
23995         /**
23996          * @event OverlayViewDraw
23997          * Fires when OverlayView Draw
23998          * @param {Roo.bootstrap.LocationPicker} this
23999          */
24000         OverlayViewDraw : true,
24001         /**
24002          * @event OverlayViewOnAdd
24003          * Fires when OverlayView Draw
24004          * @param {Roo.bootstrap.LocationPicker} this
24005          */
24006         OverlayViewOnAdd : true,
24007         /**
24008          * @event OverlayViewOnRemove
24009          * Fires when OverlayView Draw
24010          * @param {Roo.bootstrap.LocationPicker} this
24011          */
24012         OverlayViewOnRemove : true,
24013         /**
24014          * @event OverlayViewShow
24015          * Fires when OverlayView Draw
24016          * @param {Roo.bootstrap.LocationPicker} this
24017          * @param {Pixel} cpx
24018          */
24019         OverlayViewShow : true,
24020         /**
24021          * @event OverlayViewHide
24022          * Fires when OverlayView Draw
24023          * @param {Roo.bootstrap.LocationPicker} this
24024          */
24025         OverlayViewHide : true,
24026         /**
24027          * @event loadexception
24028          * Fires when load google lib failed.
24029          * @param {Roo.bootstrap.LocationPicker} this
24030          */
24031         loadexception : true
24032     });
24033         
24034 };
24035
24036 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24037     
24038     gMapContext: false,
24039     
24040     latitude: 0,
24041     longitude: 0,
24042     zoom: 15,
24043     mapTypeId: false,
24044     mapTypeControl: false,
24045     disableDoubleClickZoom: false,
24046     scrollwheel: true,
24047     streetViewControl: false,
24048     radius: 0,
24049     locationName: '',
24050     draggable: true,
24051     enableAutocomplete: false,
24052     enableReverseGeocode: true,
24053     markerTitle: '',
24054     
24055     getAutoCreate: function()
24056     {
24057
24058         var cfg = {
24059             tag: 'div',
24060             cls: 'roo-location-picker'
24061         };
24062         
24063         return cfg
24064     },
24065     
24066     initEvents: function(ct, position)
24067     {       
24068         if(!this.el.getWidth() || this.isApplied()){
24069             return;
24070         }
24071         
24072         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24073         
24074         this.initial();
24075     },
24076     
24077     initial: function()
24078     {
24079         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24080             this.fireEvent('loadexception', this);
24081             return;
24082         }
24083         
24084         if(!this.mapTypeId){
24085             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24086         }
24087         
24088         this.gMapContext = this.GMapContext();
24089         
24090         this.initOverlayView();
24091         
24092         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24093         
24094         var _this = this;
24095                 
24096         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24097             _this.setPosition(_this.gMapContext.marker.position);
24098         });
24099         
24100         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24101             _this.fireEvent('mapClick', this, event);
24102             
24103         });
24104
24105         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24106             _this.fireEvent('mapRightClick', this, event);
24107             
24108         });
24109         
24110         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24111             _this.fireEvent('markerClick', this, event);
24112             
24113         });
24114
24115         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24116             _this.fireEvent('markerRightClick', this, event);
24117             
24118         });
24119         
24120         this.setPosition(this.gMapContext.location);
24121         
24122         this.fireEvent('initial', this, this.gMapContext.location);
24123     },
24124     
24125     initOverlayView: function()
24126     {
24127         var _this = this;
24128         
24129         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24130             
24131             draw: function()
24132             {
24133                 _this.fireEvent('OverlayViewDraw', _this);
24134             },
24135             
24136             onAdd: function()
24137             {
24138                 _this.fireEvent('OverlayViewOnAdd', _this);
24139             },
24140             
24141             onRemove: function()
24142             {
24143                 _this.fireEvent('OverlayViewOnRemove', _this);
24144             },
24145             
24146             show: function(cpx)
24147             {
24148                 _this.fireEvent('OverlayViewShow', _this, cpx);
24149             },
24150             
24151             hide: function()
24152             {
24153                 _this.fireEvent('OverlayViewHide', _this);
24154             }
24155             
24156         });
24157     },
24158     
24159     fromLatLngToContainerPixel: function(event)
24160     {
24161         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24162     },
24163     
24164     isApplied: function() 
24165     {
24166         return this.getGmapContext() == false ? false : true;
24167     },
24168     
24169     getGmapContext: function() 
24170     {
24171         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24172     },
24173     
24174     GMapContext: function() 
24175     {
24176         var position = new google.maps.LatLng(this.latitude, this.longitude);
24177         
24178         var _map = new google.maps.Map(this.el.dom, {
24179             center: position,
24180             zoom: this.zoom,
24181             mapTypeId: this.mapTypeId,
24182             mapTypeControl: this.mapTypeControl,
24183             disableDoubleClickZoom: this.disableDoubleClickZoom,
24184             scrollwheel: this.scrollwheel,
24185             streetViewControl: this.streetViewControl,
24186             locationName: this.locationName,
24187             draggable: this.draggable,
24188             enableAutocomplete: this.enableAutocomplete,
24189             enableReverseGeocode: this.enableReverseGeocode
24190         });
24191         
24192         var _marker = new google.maps.Marker({
24193             position: position,
24194             map: _map,
24195             title: this.markerTitle,
24196             draggable: this.draggable
24197         });
24198         
24199         return {
24200             map: _map,
24201             marker: _marker,
24202             circle: null,
24203             location: position,
24204             radius: this.radius,
24205             locationName: this.locationName,
24206             addressComponents: {
24207                 formatted_address: null,
24208                 addressLine1: null,
24209                 addressLine2: null,
24210                 streetName: null,
24211                 streetNumber: null,
24212                 city: null,
24213                 district: null,
24214                 state: null,
24215                 stateOrProvince: null
24216             },
24217             settings: this,
24218             domContainer: this.el.dom,
24219             geodecoder: new google.maps.Geocoder()
24220         };
24221     },
24222     
24223     drawCircle: function(center, radius, options) 
24224     {
24225         if (this.gMapContext.circle != null) {
24226             this.gMapContext.circle.setMap(null);
24227         }
24228         if (radius > 0) {
24229             radius *= 1;
24230             options = Roo.apply({}, options, {
24231                 strokeColor: "#0000FF",
24232                 strokeOpacity: .35,
24233                 strokeWeight: 2,
24234                 fillColor: "#0000FF",
24235                 fillOpacity: .2
24236             });
24237             
24238             options.map = this.gMapContext.map;
24239             options.radius = radius;
24240             options.center = center;
24241             this.gMapContext.circle = new google.maps.Circle(options);
24242             return this.gMapContext.circle;
24243         }
24244         
24245         return null;
24246     },
24247     
24248     setPosition: function(location) 
24249     {
24250         this.gMapContext.location = location;
24251         this.gMapContext.marker.setPosition(location);
24252         this.gMapContext.map.panTo(location);
24253         this.drawCircle(location, this.gMapContext.radius, {});
24254         
24255         var _this = this;
24256         
24257         if (this.gMapContext.settings.enableReverseGeocode) {
24258             this.gMapContext.geodecoder.geocode({
24259                 latLng: this.gMapContext.location
24260             }, function(results, status) {
24261                 
24262                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24263                     _this.gMapContext.locationName = results[0].formatted_address;
24264                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24265                     
24266                     _this.fireEvent('positionchanged', this, location);
24267                 }
24268             });
24269             
24270             return;
24271         }
24272         
24273         this.fireEvent('positionchanged', this, location);
24274     },
24275     
24276     resize: function()
24277     {
24278         google.maps.event.trigger(this.gMapContext.map, "resize");
24279         
24280         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24281         
24282         this.fireEvent('resize', this);
24283     },
24284     
24285     setPositionByLatLng: function(latitude, longitude)
24286     {
24287         this.setPosition(new google.maps.LatLng(latitude, longitude));
24288     },
24289     
24290     getCurrentPosition: function() 
24291     {
24292         return {
24293             latitude: this.gMapContext.location.lat(),
24294             longitude: this.gMapContext.location.lng()
24295         };
24296     },
24297     
24298     getAddressName: function() 
24299     {
24300         return this.gMapContext.locationName;
24301     },
24302     
24303     getAddressComponents: function() 
24304     {
24305         return this.gMapContext.addressComponents;
24306     },
24307     
24308     address_component_from_google_geocode: function(address_components) 
24309     {
24310         var result = {};
24311         
24312         for (var i = 0; i < address_components.length; i++) {
24313             var component = address_components[i];
24314             if (component.types.indexOf("postal_code") >= 0) {
24315                 result.postalCode = component.short_name;
24316             } else if (component.types.indexOf("street_number") >= 0) {
24317                 result.streetNumber = component.short_name;
24318             } else if (component.types.indexOf("route") >= 0) {
24319                 result.streetName = component.short_name;
24320             } else if (component.types.indexOf("neighborhood") >= 0) {
24321                 result.city = component.short_name;
24322             } else if (component.types.indexOf("locality") >= 0) {
24323                 result.city = component.short_name;
24324             } else if (component.types.indexOf("sublocality") >= 0) {
24325                 result.district = component.short_name;
24326             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24327                 result.stateOrProvince = component.short_name;
24328             } else if (component.types.indexOf("country") >= 0) {
24329                 result.country = component.short_name;
24330             }
24331         }
24332         
24333         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24334         result.addressLine2 = "";
24335         return result;
24336     },
24337     
24338     setZoomLevel: function(zoom)
24339     {
24340         this.gMapContext.map.setZoom(zoom);
24341     },
24342     
24343     show: function()
24344     {
24345         if(!this.el){
24346             return;
24347         }
24348         
24349         this.el.show();
24350         
24351         this.resize();
24352         
24353         this.fireEvent('show', this);
24354     },
24355     
24356     hide: function()
24357     {
24358         if(!this.el){
24359             return;
24360         }
24361         
24362         this.el.hide();
24363         
24364         this.fireEvent('hide', this);
24365     }
24366     
24367 });
24368
24369 Roo.apply(Roo.bootstrap.LocationPicker, {
24370     
24371     OverlayView : function(map, options)
24372     {
24373         options = options || {};
24374         
24375         this.setMap(map);
24376     }
24377     
24378     
24379 });/*
24380  * - LGPL
24381  *
24382  * Alert
24383  * 
24384  */
24385
24386 /**
24387  * @class Roo.bootstrap.Alert
24388  * @extends Roo.bootstrap.Component
24389  * Bootstrap Alert class
24390  * @cfg {String} title The title of alert
24391  * @cfg {String} html The content of alert
24392  * @cfg {String} weight (  success | info | warning | danger )
24393  * @cfg {String} faicon font-awesomeicon
24394  * 
24395  * @constructor
24396  * Create a new alert
24397  * @param {Object} config The config object
24398  */
24399
24400
24401 Roo.bootstrap.Alert = function(config){
24402     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24403     
24404 };
24405
24406 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24407     
24408     title: '',
24409     html: '',
24410     weight: false,
24411     faicon: false,
24412     
24413     getAutoCreate : function()
24414     {
24415         
24416         var cfg = {
24417             tag : 'div',
24418             cls : 'alert',
24419             cn : [
24420                 {
24421                     tag : 'i',
24422                     cls : 'roo-alert-icon'
24423                     
24424                 },
24425                 {
24426                     tag : 'b',
24427                     cls : 'roo-alert-title',
24428                     html : this.title
24429                 },
24430                 {
24431                     tag : 'span',
24432                     cls : 'roo-alert-text',
24433                     html : this.html
24434                 }
24435             ]
24436         };
24437         
24438         if(this.faicon){
24439             cfg.cn[0].cls += ' fa ' + this.faicon;
24440         }
24441         
24442         if(this.weight){
24443             cfg.cls += ' alert-' + this.weight;
24444         }
24445         
24446         return cfg;
24447     },
24448     
24449     initEvents: function() 
24450     {
24451         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24452     },
24453     
24454     setTitle : function(str)
24455     {
24456         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24457     },
24458     
24459     setText : function(str)
24460     {
24461         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24462     },
24463     
24464     setWeight : function(weight)
24465     {
24466         if(this.weight){
24467             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24468         }
24469         
24470         this.weight = weight;
24471         
24472         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24473     },
24474     
24475     setIcon : function(icon)
24476     {
24477         if(this.faicon){
24478             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24479         }
24480         
24481         this.faicon = icon;
24482         
24483         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24484     },
24485     
24486     hide: function() 
24487     {
24488         this.el.hide();   
24489     },
24490     
24491     show: function() 
24492     {  
24493         this.el.show();   
24494     }
24495     
24496 });
24497
24498  
24499 /*
24500 * Licence: LGPL
24501 */
24502
24503 /**
24504  * @class Roo.bootstrap.UploadCropbox
24505  * @extends Roo.bootstrap.Component
24506  * Bootstrap UploadCropbox class
24507  * @cfg {String} emptyText show when image has been loaded
24508  * @cfg {String} rotateNotify show when image too small to rotate
24509  * @cfg {Number} errorTimeout default 3000
24510  * @cfg {Number} minWidth default 300
24511  * @cfg {Number} minHeight default 300
24512  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24513  * @cfg {Boolean} isDocument (true|false) default false
24514  * @cfg {String} url action url
24515  * @cfg {String} paramName default 'imageUpload'
24516  * @cfg {String} method default POST
24517  * @cfg {Boolean} loadMask (true|false) default true
24518  * @cfg {Boolean} loadingText default 'Loading...'
24519  * 
24520  * @constructor
24521  * Create a new UploadCropbox
24522  * @param {Object} config The config object
24523  */
24524
24525 Roo.bootstrap.UploadCropbox = function(config){
24526     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24527     
24528     this.addEvents({
24529         /**
24530          * @event beforeselectfile
24531          * Fire before select file
24532          * @param {Roo.bootstrap.UploadCropbox} this
24533          */
24534         "beforeselectfile" : true,
24535         /**
24536          * @event initial
24537          * Fire after initEvent
24538          * @param {Roo.bootstrap.UploadCropbox} this
24539          */
24540         "initial" : true,
24541         /**
24542          * @event crop
24543          * Fire after initEvent
24544          * @param {Roo.bootstrap.UploadCropbox} this
24545          * @param {String} data
24546          */
24547         "crop" : true,
24548         /**
24549          * @event prepare
24550          * Fire when preparing the file data
24551          * @param {Roo.bootstrap.UploadCropbox} this
24552          * @param {Object} file
24553          */
24554         "prepare" : true,
24555         /**
24556          * @event exception
24557          * Fire when get exception
24558          * @param {Roo.bootstrap.UploadCropbox} this
24559          * @param {XMLHttpRequest} xhr
24560          */
24561         "exception" : true,
24562         /**
24563          * @event beforeloadcanvas
24564          * Fire before load the canvas
24565          * @param {Roo.bootstrap.UploadCropbox} this
24566          * @param {String} src
24567          */
24568         "beforeloadcanvas" : true,
24569         /**
24570          * @event trash
24571          * Fire when trash image
24572          * @param {Roo.bootstrap.UploadCropbox} this
24573          */
24574         "trash" : true,
24575         /**
24576          * @event download
24577          * Fire when download the image
24578          * @param {Roo.bootstrap.UploadCropbox} this
24579          */
24580         "download" : true,
24581         /**
24582          * @event footerbuttonclick
24583          * Fire when footerbuttonclick
24584          * @param {Roo.bootstrap.UploadCropbox} this
24585          * @param {String} type
24586          */
24587         "footerbuttonclick" : true,
24588         /**
24589          * @event resize
24590          * Fire when resize
24591          * @param {Roo.bootstrap.UploadCropbox} this
24592          */
24593         "resize" : true,
24594         /**
24595          * @event rotate
24596          * Fire when rotate the image
24597          * @param {Roo.bootstrap.UploadCropbox} this
24598          * @param {String} pos
24599          */
24600         "rotate" : true,
24601         /**
24602          * @event inspect
24603          * Fire when inspect the file
24604          * @param {Roo.bootstrap.UploadCropbox} this
24605          * @param {Object} file
24606          */
24607         "inspect" : true,
24608         /**
24609          * @event upload
24610          * Fire when xhr upload the file
24611          * @param {Roo.bootstrap.UploadCropbox} this
24612          * @param {Object} data
24613          */
24614         "upload" : true,
24615         /**
24616          * @event arrange
24617          * Fire when arrange the file data
24618          * @param {Roo.bootstrap.UploadCropbox} this
24619          * @param {Object} formData
24620          */
24621         "arrange" : true
24622     });
24623     
24624     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24625 };
24626
24627 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24628     
24629     emptyText : 'Click to upload image',
24630     rotateNotify : 'Image is too small to rotate',
24631     errorTimeout : 3000,
24632     scale : 0,
24633     baseScale : 1,
24634     rotate : 0,
24635     dragable : false,
24636     pinching : false,
24637     mouseX : 0,
24638     mouseY : 0,
24639     cropData : false,
24640     minWidth : 300,
24641     minHeight : 300,
24642     file : false,
24643     exif : {},
24644     baseRotate : 1,
24645     cropType : 'image/jpeg',
24646     buttons : false,
24647     canvasLoaded : false,
24648     isDocument : false,
24649     method : 'POST',
24650     paramName : 'imageUpload',
24651     loadMask : true,
24652     loadingText : 'Loading...',
24653     maskEl : false,
24654     
24655     getAutoCreate : function()
24656     {
24657         var cfg = {
24658             tag : 'div',
24659             cls : 'roo-upload-cropbox',
24660             cn : [
24661                 {
24662                     tag : 'input',
24663                     cls : 'roo-upload-cropbox-selector',
24664                     type : 'file'
24665                 },
24666                 {
24667                     tag : 'div',
24668                     cls : 'roo-upload-cropbox-body',
24669                     style : 'cursor:pointer',
24670                     cn : [
24671                         {
24672                             tag : 'div',
24673                             cls : 'roo-upload-cropbox-preview'
24674                         },
24675                         {
24676                             tag : 'div',
24677                             cls : 'roo-upload-cropbox-thumb'
24678                         },
24679                         {
24680                             tag : 'div',
24681                             cls : 'roo-upload-cropbox-empty-notify',
24682                             html : this.emptyText
24683                         },
24684                         {
24685                             tag : 'div',
24686                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24687                             html : this.rotateNotify
24688                         }
24689                     ]
24690                 },
24691                 {
24692                     tag : 'div',
24693                     cls : 'roo-upload-cropbox-footer',
24694                     cn : {
24695                         tag : 'div',
24696                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24697                         cn : []
24698                     }
24699                 }
24700             ]
24701         };
24702         
24703         return cfg;
24704     },
24705     
24706     onRender : function(ct, position)
24707     {
24708         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24709         
24710         if (this.buttons.length) {
24711             
24712             Roo.each(this.buttons, function(bb) {
24713                 
24714                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24715                 
24716                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24717                 
24718             }, this);
24719         }
24720         
24721         if(this.loadMask){
24722             this.maskEl = this.el;
24723         }
24724     },
24725     
24726     initEvents : function()
24727     {
24728         this.urlAPI = (window.createObjectURL && window) || 
24729                                 (window.URL && URL.revokeObjectURL && URL) || 
24730                                 (window.webkitURL && webkitURL);
24731                         
24732         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24733         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24734         
24735         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24736         this.selectorEl.hide();
24737         
24738         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24739         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24740         
24741         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24742         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24743         this.thumbEl.hide();
24744         
24745         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24746         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24747         
24748         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24749         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24750         this.errorEl.hide();
24751         
24752         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24753         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24754         this.footerEl.hide();
24755         
24756         this.setThumbBoxSize();
24757         
24758         this.bind();
24759         
24760         this.resize();
24761         
24762         this.fireEvent('initial', this);
24763     },
24764
24765     bind : function()
24766     {
24767         var _this = this;
24768         
24769         window.addEventListener("resize", function() { _this.resize(); } );
24770         
24771         this.bodyEl.on('click', this.beforeSelectFile, this);
24772         
24773         if(Roo.isTouch){
24774             this.bodyEl.on('touchstart', this.onTouchStart, this);
24775             this.bodyEl.on('touchmove', this.onTouchMove, this);
24776             this.bodyEl.on('touchend', this.onTouchEnd, this);
24777         }
24778         
24779         if(!Roo.isTouch){
24780             this.bodyEl.on('mousedown', this.onMouseDown, this);
24781             this.bodyEl.on('mousemove', this.onMouseMove, this);
24782             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24783             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24784             Roo.get(document).on('mouseup', this.onMouseUp, this);
24785         }
24786         
24787         this.selectorEl.on('change', this.onFileSelected, this);
24788     },
24789     
24790     reset : function()
24791     {    
24792         this.scale = 0;
24793         this.baseScale = 1;
24794         this.rotate = 0;
24795         this.baseRotate = 1;
24796         this.dragable = false;
24797         this.pinching = false;
24798         this.mouseX = 0;
24799         this.mouseY = 0;
24800         this.cropData = false;
24801         this.notifyEl.dom.innerHTML = this.emptyText;
24802         
24803         this.selectorEl.dom.value = '';
24804         
24805     },
24806     
24807     resize : function()
24808     {
24809         if(this.fireEvent('resize', this) != false){
24810             this.setThumbBoxPosition();
24811             this.setCanvasPosition();
24812         }
24813     },
24814     
24815     onFooterButtonClick : function(e, el, o, type)
24816     {
24817         switch (type) {
24818             case 'rotate-left' :
24819                 this.onRotateLeft(e);
24820                 break;
24821             case 'rotate-right' :
24822                 this.onRotateRight(e);
24823                 break;
24824             case 'picture' :
24825                 this.beforeSelectFile(e);
24826                 break;
24827             case 'trash' :
24828                 this.trash(e);
24829                 break;
24830             case 'crop' :
24831                 this.crop(e);
24832                 break;
24833             case 'download' :
24834                 this.download(e);
24835                 break;
24836             default :
24837                 break;
24838         }
24839         
24840         this.fireEvent('footerbuttonclick', this, type);
24841     },
24842     
24843     beforeSelectFile : function(e)
24844     {
24845         e.preventDefault();
24846         
24847         if(this.fireEvent('beforeselectfile', this) != false){
24848             this.selectorEl.dom.click();
24849         }
24850     },
24851     
24852     onFileSelected : function(e)
24853     {
24854         e.preventDefault();
24855         
24856         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24857             return;
24858         }
24859         
24860         var file = this.selectorEl.dom.files[0];
24861         
24862         if(this.fireEvent('inspect', this, file) != false){
24863             this.prepare(file);
24864         }
24865         
24866     },
24867     
24868     trash : function(e)
24869     {
24870         this.fireEvent('trash', this);
24871     },
24872     
24873     download : function(e)
24874     {
24875         this.fireEvent('download', this);
24876     },
24877     
24878     loadCanvas : function(src)
24879     {   
24880         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24881             
24882             this.reset();
24883             
24884             this.imageEl = document.createElement('img');
24885             
24886             var _this = this;
24887             
24888             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24889             
24890             this.imageEl.src = src;
24891         }
24892     },
24893     
24894     onLoadCanvas : function()
24895     {   
24896         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24897         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24898         
24899         this.bodyEl.un('click', this.beforeSelectFile, this);
24900         
24901         this.notifyEl.hide();
24902         this.thumbEl.show();
24903         this.footerEl.show();
24904         
24905         this.baseRotateLevel();
24906         
24907         if(this.isDocument){
24908             this.setThumbBoxSize();
24909         }
24910         
24911         this.setThumbBoxPosition();
24912         
24913         this.baseScaleLevel();
24914         
24915         this.draw();
24916         
24917         this.resize();
24918         
24919         this.canvasLoaded = true;
24920         
24921         if(this.loadMask){
24922             this.maskEl.unmask();
24923         }
24924         
24925     },
24926     
24927     setCanvasPosition : function()
24928     {   
24929         if(!this.canvasEl){
24930             return;
24931         }
24932         
24933         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24934         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24935         
24936         this.previewEl.setLeft(pw);
24937         this.previewEl.setTop(ph);
24938         
24939     },
24940     
24941     onMouseDown : function(e)
24942     {   
24943         e.stopEvent();
24944         
24945         this.dragable = true;
24946         this.pinching = false;
24947         
24948         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24949             this.dragable = false;
24950             return;
24951         }
24952         
24953         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24954         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24955         
24956     },
24957     
24958     onMouseMove : function(e)
24959     {   
24960         e.stopEvent();
24961         
24962         if(!this.canvasLoaded){
24963             return;
24964         }
24965         
24966         if (!this.dragable){
24967             return;
24968         }
24969         
24970         var minX = Math.ceil(this.thumbEl.getLeft(true));
24971         var minY = Math.ceil(this.thumbEl.getTop(true));
24972         
24973         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24974         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24975         
24976         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24977         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24978         
24979         x = x - this.mouseX;
24980         y = y - this.mouseY;
24981         
24982         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24983         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24984         
24985         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24986         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24987         
24988         this.previewEl.setLeft(bgX);
24989         this.previewEl.setTop(bgY);
24990         
24991         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24992         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24993     },
24994     
24995     onMouseUp : function(e)
24996     {   
24997         e.stopEvent();
24998         
24999         this.dragable = false;
25000     },
25001     
25002     onMouseWheel : function(e)
25003     {   
25004         e.stopEvent();
25005         
25006         this.startScale = this.scale;
25007         
25008         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25009         
25010         if(!this.zoomable()){
25011             this.scale = this.startScale;
25012             return;
25013         }
25014         
25015         this.draw();
25016         
25017         return;
25018     },
25019     
25020     zoomable : function()
25021     {
25022         var minScale = this.thumbEl.getWidth() / this.minWidth;
25023         
25024         if(this.minWidth < this.minHeight){
25025             minScale = this.thumbEl.getHeight() / this.minHeight;
25026         }
25027         
25028         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25029         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25030         
25031         if(
25032                 this.isDocument &&
25033                 (this.rotate == 0 || this.rotate == 180) && 
25034                 (
25035                     width > this.imageEl.OriginWidth || 
25036                     height > this.imageEl.OriginHeight ||
25037                     (width < this.minWidth && height < this.minHeight)
25038                 )
25039         ){
25040             return false;
25041         }
25042         
25043         if(
25044                 this.isDocument &&
25045                 (this.rotate == 90 || this.rotate == 270) && 
25046                 (
25047                     width > this.imageEl.OriginWidth || 
25048                     height > this.imageEl.OriginHeight ||
25049                     (width < this.minHeight && height < this.minWidth)
25050                 )
25051         ){
25052             return false;
25053         }
25054         
25055         if(
25056                 !this.isDocument &&
25057                 (this.rotate == 0 || this.rotate == 180) && 
25058                 (
25059                     width < this.minWidth || 
25060                     width > this.imageEl.OriginWidth || 
25061                     height < this.minHeight || 
25062                     height > this.imageEl.OriginHeight
25063                 )
25064         ){
25065             return false;
25066         }
25067         
25068         if(
25069                 !this.isDocument &&
25070                 (this.rotate == 90 || this.rotate == 270) && 
25071                 (
25072                     width < this.minHeight || 
25073                     width > this.imageEl.OriginWidth || 
25074                     height < this.minWidth || 
25075                     height > this.imageEl.OriginHeight
25076                 )
25077         ){
25078             return false;
25079         }
25080         
25081         return true;
25082         
25083     },
25084     
25085     onRotateLeft : function(e)
25086     {   
25087         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25088             
25089             var minScale = this.thumbEl.getWidth() / this.minWidth;
25090             
25091             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25092             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25093             
25094             this.startScale = this.scale;
25095             
25096             while (this.getScaleLevel() < minScale){
25097             
25098                 this.scale = this.scale + 1;
25099                 
25100                 if(!this.zoomable()){
25101                     break;
25102                 }
25103                 
25104                 if(
25105                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25106                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25107                 ){
25108                     continue;
25109                 }
25110                 
25111                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25112
25113                 this.draw();
25114                 
25115                 return;
25116             }
25117             
25118             this.scale = this.startScale;
25119             
25120             this.onRotateFail();
25121             
25122             return false;
25123         }
25124         
25125         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25126
25127         if(this.isDocument){
25128             this.setThumbBoxSize();
25129             this.setThumbBoxPosition();
25130             this.setCanvasPosition();
25131         }
25132         
25133         this.draw();
25134         
25135         this.fireEvent('rotate', this, 'left');
25136         
25137     },
25138     
25139     onRotateRight : function(e)
25140     {
25141         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25142             
25143             var minScale = this.thumbEl.getWidth() / this.minWidth;
25144         
25145             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25146             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25147             
25148             this.startScale = this.scale;
25149             
25150             while (this.getScaleLevel() < minScale){
25151             
25152                 this.scale = this.scale + 1;
25153                 
25154                 if(!this.zoomable()){
25155                     break;
25156                 }
25157                 
25158                 if(
25159                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25160                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25161                 ){
25162                     continue;
25163                 }
25164                 
25165                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25166
25167                 this.draw();
25168                 
25169                 return;
25170             }
25171             
25172             this.scale = this.startScale;
25173             
25174             this.onRotateFail();
25175             
25176             return false;
25177         }
25178         
25179         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25180
25181         if(this.isDocument){
25182             this.setThumbBoxSize();
25183             this.setThumbBoxPosition();
25184             this.setCanvasPosition();
25185         }
25186         
25187         this.draw();
25188         
25189         this.fireEvent('rotate', this, 'right');
25190     },
25191     
25192     onRotateFail : function()
25193     {
25194         this.errorEl.show(true);
25195         
25196         var _this = this;
25197         
25198         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25199     },
25200     
25201     draw : function()
25202     {
25203         this.previewEl.dom.innerHTML = '';
25204         
25205         var canvasEl = document.createElement("canvas");
25206         
25207         var contextEl = canvasEl.getContext("2d");
25208         
25209         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25210         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25211         var center = this.imageEl.OriginWidth / 2;
25212         
25213         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25214             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25215             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25216             center = this.imageEl.OriginHeight / 2;
25217         }
25218         
25219         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25220         
25221         contextEl.translate(center, center);
25222         contextEl.rotate(this.rotate * Math.PI / 180);
25223
25224         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25225         
25226         this.canvasEl = document.createElement("canvas");
25227         
25228         this.contextEl = this.canvasEl.getContext("2d");
25229         
25230         switch (this.rotate) {
25231             case 0 :
25232                 
25233                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25234                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25235                 
25236                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25237                 
25238                 break;
25239             case 90 : 
25240                 
25241                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25242                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25243                 
25244                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25245                     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);
25246                     break;
25247                 }
25248                 
25249                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25250                 
25251                 break;
25252             case 180 :
25253                 
25254                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25255                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25256                 
25257                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25258                     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);
25259                     break;
25260                 }
25261                 
25262                 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);
25263                 
25264                 break;
25265             case 270 :
25266                 
25267                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25268                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25269         
25270                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25271                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25272                     break;
25273                 }
25274                 
25275                 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);
25276                 
25277                 break;
25278             default : 
25279                 break;
25280         }
25281         
25282         this.previewEl.appendChild(this.canvasEl);
25283         
25284         this.setCanvasPosition();
25285     },
25286     
25287     crop : function()
25288     {
25289         if(!this.canvasLoaded){
25290             return;
25291         }
25292         
25293         var imageCanvas = document.createElement("canvas");
25294         
25295         var imageContext = imageCanvas.getContext("2d");
25296         
25297         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25298         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25299         
25300         var center = imageCanvas.width / 2;
25301         
25302         imageContext.translate(center, center);
25303         
25304         imageContext.rotate(this.rotate * Math.PI / 180);
25305         
25306         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25307         
25308         var canvas = document.createElement("canvas");
25309         
25310         var context = canvas.getContext("2d");
25311                 
25312         canvas.width = this.minWidth;
25313         canvas.height = this.minHeight;
25314
25315         switch (this.rotate) {
25316             case 0 :
25317                 
25318                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25319                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25320                 
25321                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25322                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25323                 
25324                 var targetWidth = this.minWidth - 2 * x;
25325                 var targetHeight = this.minHeight - 2 * y;
25326                 
25327                 var scale = 1;
25328                 
25329                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25330                     scale = targetWidth / width;
25331                 }
25332                 
25333                 if(x > 0 && y == 0){
25334                     scale = targetHeight / height;
25335                 }
25336                 
25337                 if(x > 0 && y > 0){
25338                     scale = targetWidth / width;
25339                     
25340                     if(width < height){
25341                         scale = targetHeight / height;
25342                     }
25343                 }
25344                 
25345                 context.scale(scale, scale);
25346                 
25347                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25348                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25349
25350                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25351                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25352
25353                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25354                 
25355                 break;
25356             case 90 : 
25357                 
25358                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25359                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25360                 
25361                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25362                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25363                 
25364                 var targetWidth = this.minWidth - 2 * x;
25365                 var targetHeight = this.minHeight - 2 * y;
25366                 
25367                 var scale = 1;
25368                 
25369                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25370                     scale = targetWidth / width;
25371                 }
25372                 
25373                 if(x > 0 && y == 0){
25374                     scale = targetHeight / height;
25375                 }
25376                 
25377                 if(x > 0 && y > 0){
25378                     scale = targetWidth / width;
25379                     
25380                     if(width < height){
25381                         scale = targetHeight / height;
25382                     }
25383                 }
25384                 
25385                 context.scale(scale, scale);
25386                 
25387                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25388                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25389
25390                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25391                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25392                 
25393                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25394                 
25395                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25396                 
25397                 break;
25398             case 180 :
25399                 
25400                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25401                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25402                 
25403                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25404                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25405                 
25406                 var targetWidth = this.minWidth - 2 * x;
25407                 var targetHeight = this.minHeight - 2 * y;
25408                 
25409                 var scale = 1;
25410                 
25411                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25412                     scale = targetWidth / width;
25413                 }
25414                 
25415                 if(x > 0 && y == 0){
25416                     scale = targetHeight / height;
25417                 }
25418                 
25419                 if(x > 0 && y > 0){
25420                     scale = targetWidth / width;
25421                     
25422                     if(width < height){
25423                         scale = targetHeight / height;
25424                     }
25425                 }
25426                 
25427                 context.scale(scale, scale);
25428                 
25429                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25430                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25431
25432                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25433                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25434
25435                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25436                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25437                 
25438                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25439                 
25440                 break;
25441             case 270 :
25442                 
25443                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25444                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25445                 
25446                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25447                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25448                 
25449                 var targetWidth = this.minWidth - 2 * x;
25450                 var targetHeight = this.minHeight - 2 * y;
25451                 
25452                 var scale = 1;
25453                 
25454                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25455                     scale = targetWidth / width;
25456                 }
25457                 
25458                 if(x > 0 && y == 0){
25459                     scale = targetHeight / height;
25460                 }
25461                 
25462                 if(x > 0 && y > 0){
25463                     scale = targetWidth / width;
25464                     
25465                     if(width < height){
25466                         scale = targetHeight / height;
25467                     }
25468                 }
25469                 
25470                 context.scale(scale, scale);
25471                 
25472                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25473                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25474
25475                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25476                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25477                 
25478                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25479                 
25480                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25481                 
25482                 break;
25483             default : 
25484                 break;
25485         }
25486         
25487         this.cropData = canvas.toDataURL(this.cropType);
25488         
25489         if(this.fireEvent('crop', this, this.cropData) !== false){
25490             this.process(this.file, this.cropData);
25491         }
25492         
25493         return;
25494         
25495     },
25496     
25497     setThumbBoxSize : function()
25498     {
25499         var width, height;
25500         
25501         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25502             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25503             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25504             
25505             this.minWidth = width;
25506             this.minHeight = height;
25507             
25508             if(this.rotate == 90 || this.rotate == 270){
25509                 this.minWidth = height;
25510                 this.minHeight = width;
25511             }
25512         }
25513         
25514         height = 300;
25515         width = Math.ceil(this.minWidth * height / this.minHeight);
25516         
25517         if(this.minWidth > this.minHeight){
25518             width = 300;
25519             height = Math.ceil(this.minHeight * width / this.minWidth);
25520         }
25521         
25522         this.thumbEl.setStyle({
25523             width : width + 'px',
25524             height : height + 'px'
25525         });
25526
25527         return;
25528             
25529     },
25530     
25531     setThumbBoxPosition : function()
25532     {
25533         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25534         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25535         
25536         this.thumbEl.setLeft(x);
25537         this.thumbEl.setTop(y);
25538         
25539     },
25540     
25541     baseRotateLevel : function()
25542     {
25543         this.baseRotate = 1;
25544         
25545         if(
25546                 typeof(this.exif) != 'undefined' &&
25547                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25548                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25549         ){
25550             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25551         }
25552         
25553         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25554         
25555     },
25556     
25557     baseScaleLevel : function()
25558     {
25559         var width, height;
25560         
25561         if(this.isDocument){
25562             
25563             if(this.baseRotate == 6 || this.baseRotate == 8){
25564             
25565                 height = this.thumbEl.getHeight();
25566                 this.baseScale = height / this.imageEl.OriginWidth;
25567
25568                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25569                     width = this.thumbEl.getWidth();
25570                     this.baseScale = width / this.imageEl.OriginHeight;
25571                 }
25572
25573                 return;
25574             }
25575
25576             height = this.thumbEl.getHeight();
25577             this.baseScale = height / this.imageEl.OriginHeight;
25578
25579             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25580                 width = this.thumbEl.getWidth();
25581                 this.baseScale = width / this.imageEl.OriginWidth;
25582             }
25583
25584             return;
25585         }
25586         
25587         if(this.baseRotate == 6 || this.baseRotate == 8){
25588             
25589             width = this.thumbEl.getHeight();
25590             this.baseScale = width / this.imageEl.OriginHeight;
25591             
25592             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25593                 height = this.thumbEl.getWidth();
25594                 this.baseScale = height / this.imageEl.OriginHeight;
25595             }
25596             
25597             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25598                 height = this.thumbEl.getWidth();
25599                 this.baseScale = height / this.imageEl.OriginHeight;
25600                 
25601                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25602                     width = this.thumbEl.getHeight();
25603                     this.baseScale = width / this.imageEl.OriginWidth;
25604                 }
25605             }
25606             
25607             return;
25608         }
25609         
25610         width = this.thumbEl.getWidth();
25611         this.baseScale = width / this.imageEl.OriginWidth;
25612         
25613         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25614             height = this.thumbEl.getHeight();
25615             this.baseScale = height / this.imageEl.OriginHeight;
25616         }
25617         
25618         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25619             
25620             height = this.thumbEl.getHeight();
25621             this.baseScale = height / this.imageEl.OriginHeight;
25622             
25623             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25624                 width = this.thumbEl.getWidth();
25625                 this.baseScale = width / this.imageEl.OriginWidth;
25626             }
25627             
25628         }
25629         
25630         return;
25631     },
25632     
25633     getScaleLevel : function()
25634     {
25635         return this.baseScale * Math.pow(1.1, this.scale);
25636     },
25637     
25638     onTouchStart : function(e)
25639     {
25640         if(!this.canvasLoaded){
25641             this.beforeSelectFile(e);
25642             return;
25643         }
25644         
25645         var touches = e.browserEvent.touches;
25646         
25647         if(!touches){
25648             return;
25649         }
25650         
25651         if(touches.length == 1){
25652             this.onMouseDown(e);
25653             return;
25654         }
25655         
25656         if(touches.length != 2){
25657             return;
25658         }
25659         
25660         var coords = [];
25661         
25662         for(var i = 0, finger; finger = touches[i]; i++){
25663             coords.push(finger.pageX, finger.pageY);
25664         }
25665         
25666         var x = Math.pow(coords[0] - coords[2], 2);
25667         var y = Math.pow(coords[1] - coords[3], 2);
25668         
25669         this.startDistance = Math.sqrt(x + y);
25670         
25671         this.startScale = this.scale;
25672         
25673         this.pinching = true;
25674         this.dragable = false;
25675         
25676     },
25677     
25678     onTouchMove : function(e)
25679     {
25680         if(!this.pinching && !this.dragable){
25681             return;
25682         }
25683         
25684         var touches = e.browserEvent.touches;
25685         
25686         if(!touches){
25687             return;
25688         }
25689         
25690         if(this.dragable){
25691             this.onMouseMove(e);
25692             return;
25693         }
25694         
25695         var coords = [];
25696         
25697         for(var i = 0, finger; finger = touches[i]; i++){
25698             coords.push(finger.pageX, finger.pageY);
25699         }
25700         
25701         var x = Math.pow(coords[0] - coords[2], 2);
25702         var y = Math.pow(coords[1] - coords[3], 2);
25703         
25704         this.endDistance = Math.sqrt(x + y);
25705         
25706         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25707         
25708         if(!this.zoomable()){
25709             this.scale = this.startScale;
25710             return;
25711         }
25712         
25713         this.draw();
25714         
25715     },
25716     
25717     onTouchEnd : function(e)
25718     {
25719         this.pinching = false;
25720         this.dragable = false;
25721         
25722     },
25723     
25724     process : function(file, crop)
25725     {
25726         if(this.loadMask){
25727             this.maskEl.mask(this.loadingText);
25728         }
25729         
25730         this.xhr = new XMLHttpRequest();
25731         
25732         file.xhr = this.xhr;
25733
25734         this.xhr.open(this.method, this.url, true);
25735         
25736         var headers = {
25737             "Accept": "application/json",
25738             "Cache-Control": "no-cache",
25739             "X-Requested-With": "XMLHttpRequest"
25740         };
25741         
25742         for (var headerName in headers) {
25743             var headerValue = headers[headerName];
25744             if (headerValue) {
25745                 this.xhr.setRequestHeader(headerName, headerValue);
25746             }
25747         }
25748         
25749         var _this = this;
25750         
25751         this.xhr.onload = function()
25752         {
25753             _this.xhrOnLoad(_this.xhr);
25754         }
25755         
25756         this.xhr.onerror = function()
25757         {
25758             _this.xhrOnError(_this.xhr);
25759         }
25760         
25761         var formData = new FormData();
25762
25763         formData.append('returnHTML', 'NO');
25764         
25765         if(crop){
25766             formData.append('crop', crop);
25767         }
25768         
25769         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25770             formData.append(this.paramName, file, file.name);
25771         }
25772         
25773         if(typeof(file.filename) != 'undefined'){
25774             formData.append('filename', file.filename);
25775         }
25776         
25777         if(typeof(file.mimetype) != 'undefined'){
25778             formData.append('mimetype', file.mimetype);
25779         }
25780         
25781         if(this.fireEvent('arrange', this, formData) != false){
25782             this.xhr.send(formData);
25783         };
25784     },
25785     
25786     xhrOnLoad : function(xhr)
25787     {
25788         if(this.loadMask){
25789             this.maskEl.unmask();
25790         }
25791         
25792         if (xhr.readyState !== 4) {
25793             this.fireEvent('exception', this, xhr);
25794             return;
25795         }
25796
25797         var response = Roo.decode(xhr.responseText);
25798         
25799         if(!response.success){
25800             this.fireEvent('exception', this, xhr);
25801             return;
25802         }
25803         
25804         var response = Roo.decode(xhr.responseText);
25805         
25806         this.fireEvent('upload', this, response);
25807         
25808     },
25809     
25810     xhrOnError : function()
25811     {
25812         if(this.loadMask){
25813             this.maskEl.unmask();
25814         }
25815         
25816         Roo.log('xhr on error');
25817         
25818         var response = Roo.decode(xhr.responseText);
25819           
25820         Roo.log(response);
25821         
25822     },
25823     
25824     prepare : function(file)
25825     {   
25826         if(this.loadMask){
25827             this.maskEl.mask(this.loadingText);
25828         }
25829         
25830         this.file = false;
25831         this.exif = {};
25832         
25833         if(typeof(file) === 'string'){
25834             this.loadCanvas(file);
25835             return;
25836         }
25837         
25838         if(!file || !this.urlAPI){
25839             return;
25840         }
25841         
25842         this.file = file;
25843         this.cropType = file.type;
25844         
25845         var _this = this;
25846         
25847         if(this.fireEvent('prepare', this, this.file) != false){
25848             
25849             var reader = new FileReader();
25850             
25851             reader.onload = function (e) {
25852                 if (e.target.error) {
25853                     Roo.log(e.target.error);
25854                     return;
25855                 }
25856                 
25857                 var buffer = e.target.result,
25858                     dataView = new DataView(buffer),
25859                     offset = 2,
25860                     maxOffset = dataView.byteLength - 4,
25861                     markerBytes,
25862                     markerLength;
25863                 
25864                 if (dataView.getUint16(0) === 0xffd8) {
25865                     while (offset < maxOffset) {
25866                         markerBytes = dataView.getUint16(offset);
25867                         
25868                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25869                             markerLength = dataView.getUint16(offset + 2) + 2;
25870                             if (offset + markerLength > dataView.byteLength) {
25871                                 Roo.log('Invalid meta data: Invalid segment size.');
25872                                 break;
25873                             }
25874                             
25875                             if(markerBytes == 0xffe1){
25876                                 _this.parseExifData(
25877                                     dataView,
25878                                     offset,
25879                                     markerLength
25880                                 );
25881                             }
25882                             
25883                             offset += markerLength;
25884                             
25885                             continue;
25886                         }
25887                         
25888                         break;
25889                     }
25890                     
25891                 }
25892                 
25893                 var url = _this.urlAPI.createObjectURL(_this.file);
25894                 
25895                 _this.loadCanvas(url);
25896                 
25897                 return;
25898             }
25899             
25900             reader.readAsArrayBuffer(this.file);
25901             
25902         }
25903         
25904     },
25905     
25906     parseExifData : function(dataView, offset, length)
25907     {
25908         var tiffOffset = offset + 10,
25909             littleEndian,
25910             dirOffset;
25911     
25912         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25913             // No Exif data, might be XMP data instead
25914             return;
25915         }
25916         
25917         // Check for the ASCII code for "Exif" (0x45786966):
25918         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25919             // No Exif data, might be XMP data instead
25920             return;
25921         }
25922         if (tiffOffset + 8 > dataView.byteLength) {
25923             Roo.log('Invalid Exif data: Invalid segment size.');
25924             return;
25925         }
25926         // Check for the two null bytes:
25927         if (dataView.getUint16(offset + 8) !== 0x0000) {
25928             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25929             return;
25930         }
25931         // Check the byte alignment:
25932         switch (dataView.getUint16(tiffOffset)) {
25933         case 0x4949:
25934             littleEndian = true;
25935             break;
25936         case 0x4D4D:
25937             littleEndian = false;
25938             break;
25939         default:
25940             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25941             return;
25942         }
25943         // Check for the TIFF tag marker (0x002A):
25944         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25945             Roo.log('Invalid Exif data: Missing TIFF marker.');
25946             return;
25947         }
25948         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25949         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25950         
25951         this.parseExifTags(
25952             dataView,
25953             tiffOffset,
25954             tiffOffset + dirOffset,
25955             littleEndian
25956         );
25957     },
25958     
25959     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25960     {
25961         var tagsNumber,
25962             dirEndOffset,
25963             i;
25964         if (dirOffset + 6 > dataView.byteLength) {
25965             Roo.log('Invalid Exif data: Invalid directory offset.');
25966             return;
25967         }
25968         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25969         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25970         if (dirEndOffset + 4 > dataView.byteLength) {
25971             Roo.log('Invalid Exif data: Invalid directory size.');
25972             return;
25973         }
25974         for (i = 0; i < tagsNumber; i += 1) {
25975             this.parseExifTag(
25976                 dataView,
25977                 tiffOffset,
25978                 dirOffset + 2 + 12 * i, // tag offset
25979                 littleEndian
25980             );
25981         }
25982         // Return the offset to the next directory:
25983         return dataView.getUint32(dirEndOffset, littleEndian);
25984     },
25985     
25986     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25987     {
25988         var tag = dataView.getUint16(offset, littleEndian);
25989         
25990         this.exif[tag] = this.getExifValue(
25991             dataView,
25992             tiffOffset,
25993             offset,
25994             dataView.getUint16(offset + 2, littleEndian), // tag type
25995             dataView.getUint32(offset + 4, littleEndian), // tag length
25996             littleEndian
25997         );
25998     },
25999     
26000     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26001     {
26002         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26003             tagSize,
26004             dataOffset,
26005             values,
26006             i,
26007             str,
26008             c;
26009     
26010         if (!tagType) {
26011             Roo.log('Invalid Exif data: Invalid tag type.');
26012             return;
26013         }
26014         
26015         tagSize = tagType.size * length;
26016         // Determine if the value is contained in the dataOffset bytes,
26017         // or if the value at the dataOffset is a pointer to the actual data:
26018         dataOffset = tagSize > 4 ?
26019                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26020         if (dataOffset + tagSize > dataView.byteLength) {
26021             Roo.log('Invalid Exif data: Invalid data offset.');
26022             return;
26023         }
26024         if (length === 1) {
26025             return tagType.getValue(dataView, dataOffset, littleEndian);
26026         }
26027         values = [];
26028         for (i = 0; i < length; i += 1) {
26029             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26030         }
26031         
26032         if (tagType.ascii) {
26033             str = '';
26034             // Concatenate the chars:
26035             for (i = 0; i < values.length; i += 1) {
26036                 c = values[i];
26037                 // Ignore the terminating NULL byte(s):
26038                 if (c === '\u0000') {
26039                     break;
26040                 }
26041                 str += c;
26042             }
26043             return str;
26044         }
26045         return values;
26046     }
26047     
26048 });
26049
26050 Roo.apply(Roo.bootstrap.UploadCropbox, {
26051     tags : {
26052         'Orientation': 0x0112
26053     },
26054     
26055     Orientation: {
26056             1: 0, //'top-left',
26057 //            2: 'top-right',
26058             3: 180, //'bottom-right',
26059 //            4: 'bottom-left',
26060 //            5: 'left-top',
26061             6: 90, //'right-top',
26062 //            7: 'right-bottom',
26063             8: 270 //'left-bottom'
26064     },
26065     
26066     exifTagTypes : {
26067         // byte, 8-bit unsigned int:
26068         1: {
26069             getValue: function (dataView, dataOffset) {
26070                 return dataView.getUint8(dataOffset);
26071             },
26072             size: 1
26073         },
26074         // ascii, 8-bit byte:
26075         2: {
26076             getValue: function (dataView, dataOffset) {
26077                 return String.fromCharCode(dataView.getUint8(dataOffset));
26078             },
26079             size: 1,
26080             ascii: true
26081         },
26082         // short, 16 bit int:
26083         3: {
26084             getValue: function (dataView, dataOffset, littleEndian) {
26085                 return dataView.getUint16(dataOffset, littleEndian);
26086             },
26087             size: 2
26088         },
26089         // long, 32 bit int:
26090         4: {
26091             getValue: function (dataView, dataOffset, littleEndian) {
26092                 return dataView.getUint32(dataOffset, littleEndian);
26093             },
26094             size: 4
26095         },
26096         // rational = two long values, first is numerator, second is denominator:
26097         5: {
26098             getValue: function (dataView, dataOffset, littleEndian) {
26099                 return dataView.getUint32(dataOffset, littleEndian) /
26100                     dataView.getUint32(dataOffset + 4, littleEndian);
26101             },
26102             size: 8
26103         },
26104         // slong, 32 bit signed int:
26105         9: {
26106             getValue: function (dataView, dataOffset, littleEndian) {
26107                 return dataView.getInt32(dataOffset, littleEndian);
26108             },
26109             size: 4
26110         },
26111         // srational, two slongs, first is numerator, second is denominator:
26112         10: {
26113             getValue: function (dataView, dataOffset, littleEndian) {
26114                 return dataView.getInt32(dataOffset, littleEndian) /
26115                     dataView.getInt32(dataOffset + 4, littleEndian);
26116             },
26117             size: 8
26118         }
26119     },
26120     
26121     footer : {
26122         STANDARD : [
26123             {
26124                 tag : 'div',
26125                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26126                 action : 'rotate-left',
26127                 cn : [
26128                     {
26129                         tag : 'button',
26130                         cls : 'btn btn-default',
26131                         html : '<i class="fa fa-undo"></i>'
26132                     }
26133                 ]
26134             },
26135             {
26136                 tag : 'div',
26137                 cls : 'btn-group roo-upload-cropbox-picture',
26138                 action : 'picture',
26139                 cn : [
26140                     {
26141                         tag : 'button',
26142                         cls : 'btn btn-default',
26143                         html : '<i class="fa fa-picture-o"></i>'
26144                     }
26145                 ]
26146             },
26147             {
26148                 tag : 'div',
26149                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26150                 action : 'rotate-right',
26151                 cn : [
26152                     {
26153                         tag : 'button',
26154                         cls : 'btn btn-default',
26155                         html : '<i class="fa fa-repeat"></i>'
26156                     }
26157                 ]
26158             }
26159         ],
26160         DOCUMENT : [
26161             {
26162                 tag : 'div',
26163                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26164                 action : 'rotate-left',
26165                 cn : [
26166                     {
26167                         tag : 'button',
26168                         cls : 'btn btn-default',
26169                         html : '<i class="fa fa-undo"></i>'
26170                     }
26171                 ]
26172             },
26173             {
26174                 tag : 'div',
26175                 cls : 'btn-group roo-upload-cropbox-download',
26176                 action : 'download',
26177                 cn : [
26178                     {
26179                         tag : 'button',
26180                         cls : 'btn btn-default',
26181                         html : '<i class="fa fa-download"></i>'
26182                     }
26183                 ]
26184             },
26185             {
26186                 tag : 'div',
26187                 cls : 'btn-group roo-upload-cropbox-crop',
26188                 action : 'crop',
26189                 cn : [
26190                     {
26191                         tag : 'button',
26192                         cls : 'btn btn-default',
26193                         html : '<i class="fa fa-crop"></i>'
26194                     }
26195                 ]
26196             },
26197             {
26198                 tag : 'div',
26199                 cls : 'btn-group roo-upload-cropbox-trash',
26200                 action : 'trash',
26201                 cn : [
26202                     {
26203                         tag : 'button',
26204                         cls : 'btn btn-default',
26205                         html : '<i class="fa fa-trash"></i>'
26206                     }
26207                 ]
26208             },
26209             {
26210                 tag : 'div',
26211                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26212                 action : 'rotate-right',
26213                 cn : [
26214                     {
26215                         tag : 'button',
26216                         cls : 'btn btn-default',
26217                         html : '<i class="fa fa-repeat"></i>'
26218                     }
26219                 ]
26220             }
26221         ],
26222         ROTATOR : [
26223             {
26224                 tag : 'div',
26225                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26226                 action : 'rotate-left',
26227                 cn : [
26228                     {
26229                         tag : 'button',
26230                         cls : 'btn btn-default',
26231                         html : '<i class="fa fa-undo"></i>'
26232                     }
26233                 ]
26234             },
26235             {
26236                 tag : 'div',
26237                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26238                 action : 'rotate-right',
26239                 cn : [
26240                     {
26241                         tag : 'button',
26242                         cls : 'btn btn-default',
26243                         html : '<i class="fa fa-repeat"></i>'
26244                     }
26245                 ]
26246             }
26247         ]
26248     }
26249 });
26250
26251 /*
26252 * Licence: LGPL
26253 */
26254
26255 /**
26256  * @class Roo.bootstrap.DocumentManager
26257  * @extends Roo.bootstrap.Component
26258  * Bootstrap DocumentManager class
26259  * @cfg {String} paramName default 'imageUpload'
26260  * @cfg {String} method default POST
26261  * @cfg {String} url action url
26262  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26263  * @cfg {Boolean} multiple multiple upload default true
26264  * @cfg {Number} thumbSize default 300
26265  * @cfg {String} fieldLabel
26266  * @cfg {Number} labelWidth default 4
26267  * @cfg {String} labelAlign (left|top) default left
26268  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26269  * 
26270  * @constructor
26271  * Create a new DocumentManager
26272  * @param {Object} config The config object
26273  */
26274
26275 Roo.bootstrap.DocumentManager = function(config){
26276     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26277     
26278     this.addEvents({
26279         /**
26280          * @event initial
26281          * Fire when initial the DocumentManager
26282          * @param {Roo.bootstrap.DocumentManager} this
26283          */
26284         "initial" : true,
26285         /**
26286          * @event inspect
26287          * inspect selected file
26288          * @param {Roo.bootstrap.DocumentManager} this
26289          * @param {File} file
26290          */
26291         "inspect" : true,
26292         /**
26293          * @event exception
26294          * Fire when xhr load exception
26295          * @param {Roo.bootstrap.DocumentManager} this
26296          * @param {XMLHttpRequest} xhr
26297          */
26298         "exception" : true,
26299         /**
26300          * @event prepare
26301          * prepare the form data
26302          * @param {Roo.bootstrap.DocumentManager} this
26303          * @param {Object} formData
26304          */
26305         "prepare" : true,
26306         /**
26307          * @event remove
26308          * Fire when remove the file
26309          * @param {Roo.bootstrap.DocumentManager} this
26310          * @param {Object} file
26311          */
26312         "remove" : true,
26313         /**
26314          * @event refresh
26315          * Fire after refresh the file
26316          * @param {Roo.bootstrap.DocumentManager} this
26317          */
26318         "refresh" : true,
26319         /**
26320          * @event click
26321          * Fire after click the image
26322          * @param {Roo.bootstrap.DocumentManager} this
26323          * @param {Object} file
26324          */
26325         "click" : true,
26326         /**
26327          * @event edit
26328          * Fire when upload a image and editable set to true
26329          * @param {Roo.bootstrap.DocumentManager} this
26330          * @param {Object} file
26331          */
26332         "edit" : true,
26333         /**
26334          * @event beforeselectfile
26335          * Fire before select file
26336          * @param {Roo.bootstrap.DocumentManager} this
26337          */
26338         "beforeselectfile" : true,
26339         /**
26340          * @event process
26341          * Fire before process file
26342          * @param {Roo.bootstrap.DocumentManager} this
26343          * @param {Object} file
26344          */
26345         "process" : true
26346         
26347     });
26348 };
26349
26350 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26351     
26352     boxes : 0,
26353     inputName : '',
26354     thumbSize : 300,
26355     multiple : true,
26356     files : [],
26357     method : 'POST',
26358     url : '',
26359     paramName : 'imageUpload',
26360     fieldLabel : '',
26361     labelWidth : 4,
26362     labelAlign : 'left',
26363     editable : true,
26364     delegates : [],
26365     
26366     
26367     xhr : false, 
26368     
26369     getAutoCreate : function()
26370     {   
26371         var managerWidget = {
26372             tag : 'div',
26373             cls : 'roo-document-manager',
26374             cn : [
26375                 {
26376                     tag : 'input',
26377                     cls : 'roo-document-manager-selector',
26378                     type : 'file'
26379                 },
26380                 {
26381                     tag : 'div',
26382                     cls : 'roo-document-manager-uploader',
26383                     cn : [
26384                         {
26385                             tag : 'div',
26386                             cls : 'roo-document-manager-upload-btn',
26387                             html : '<i class="fa fa-plus"></i>'
26388                         }
26389                     ]
26390                     
26391                 }
26392             ]
26393         };
26394         
26395         var content = [
26396             {
26397                 tag : 'div',
26398                 cls : 'column col-md-12',
26399                 cn : managerWidget
26400             }
26401         ];
26402         
26403         if(this.fieldLabel.length){
26404             
26405             content = [
26406                 {
26407                     tag : 'div',
26408                     cls : 'column col-md-12',
26409                     html : this.fieldLabel
26410                 },
26411                 {
26412                     tag : 'div',
26413                     cls : 'column col-md-12',
26414                     cn : managerWidget
26415                 }
26416             ];
26417
26418             if(this.labelAlign == 'left'){
26419                 content = [
26420                     {
26421                         tag : 'div',
26422                         cls : 'column col-md-' + this.labelWidth,
26423                         html : this.fieldLabel
26424                     },
26425                     {
26426                         tag : 'div',
26427                         cls : 'column col-md-' + (12 - this.labelWidth),
26428                         cn : managerWidget
26429                     }
26430                 ];
26431                 
26432             }
26433         }
26434         
26435         var cfg = {
26436             tag : 'div',
26437             cls : 'row clearfix',
26438             cn : content
26439         };
26440         
26441         return cfg;
26442         
26443     },
26444     
26445     initEvents : function()
26446     {
26447         this.managerEl = this.el.select('.roo-document-manager', true).first();
26448         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26449         
26450         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26451         this.selectorEl.hide();
26452         
26453         if(this.multiple){
26454             this.selectorEl.attr('multiple', 'multiple');
26455         }
26456         
26457         this.selectorEl.on('change', this.onFileSelected, this);
26458         
26459         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26460         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26461         
26462         this.uploader.on('click', this.onUploaderClick, this);
26463         
26464         this.renderProgressDialog();
26465         
26466         var _this = this;
26467         
26468         window.addEventListener("resize", function() { _this.refresh(); } );
26469         
26470         this.fireEvent('initial', this);
26471     },
26472     
26473     renderProgressDialog : function()
26474     {
26475         var _this = this;
26476         
26477         this.progressDialog = new Roo.bootstrap.Modal({
26478             cls : 'roo-document-manager-progress-dialog',
26479             allow_close : false,
26480             title : '',
26481             buttons : [
26482                 {
26483                     name  :'cancel',
26484                     weight : 'danger',
26485                     html : 'Cancel'
26486                 }
26487             ], 
26488             listeners : { 
26489                 btnclick : function() {
26490                     _this.uploadCancel();
26491                     this.hide();
26492                 }
26493             }
26494         });
26495          
26496         this.progressDialog.render(Roo.get(document.body));
26497          
26498         this.progress = new Roo.bootstrap.Progress({
26499             cls : 'roo-document-manager-progress',
26500             active : true,
26501             striped : true
26502         });
26503         
26504         this.progress.render(this.progressDialog.getChildContainer());
26505         
26506         this.progressBar = new Roo.bootstrap.ProgressBar({
26507             cls : 'roo-document-manager-progress-bar',
26508             aria_valuenow : 0,
26509             aria_valuemin : 0,
26510             aria_valuemax : 12,
26511             panel : 'success'
26512         });
26513         
26514         this.progressBar.render(this.progress.getChildContainer());
26515     },
26516     
26517     onUploaderClick : function(e)
26518     {
26519         e.preventDefault();
26520      
26521         if(this.fireEvent('beforeselectfile', this) != false){
26522             this.selectorEl.dom.click();
26523         }
26524         
26525     },
26526     
26527     onFileSelected : function(e)
26528     {
26529         e.preventDefault();
26530         
26531         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26532             return;
26533         }
26534         
26535         Roo.each(this.selectorEl.dom.files, function(file){
26536             if(this.fireEvent('inspect', this, file) != false){
26537                 this.files.push(file);
26538             }
26539         }, this);
26540         
26541         this.queue();
26542         
26543     },
26544     
26545     queue : function()
26546     {
26547         this.selectorEl.dom.value = '';
26548         
26549         if(!this.files.length){
26550             return;
26551         }
26552         
26553         if(this.boxes > 0 && this.files.length > this.boxes){
26554             this.files = this.files.slice(0, this.boxes);
26555         }
26556         
26557         this.uploader.show();
26558         
26559         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26560             this.uploader.hide();
26561         }
26562         
26563         var _this = this;
26564         
26565         var files = [];
26566         
26567         var docs = [];
26568         
26569         Roo.each(this.files, function(file){
26570             
26571             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26572                 var f = this.renderPreview(file);
26573                 files.push(f);
26574                 return;
26575             }
26576             
26577             if(file.type.indexOf('image') != -1){
26578                 this.delegates.push(
26579                     (function(){
26580                         _this.process(file);
26581                     }).createDelegate(this)
26582                 );
26583         
26584                 return;
26585             }
26586             
26587             docs.push(
26588                 (function(){
26589                     _this.process(file);
26590                 }).createDelegate(this)
26591             );
26592             
26593         }, this);
26594         
26595         this.files = files;
26596         
26597         this.delegates = this.delegates.concat(docs);
26598         
26599         if(!this.delegates.length){
26600             this.refresh();
26601             return;
26602         }
26603         
26604         this.progressBar.aria_valuemax = this.delegates.length;
26605         
26606         this.arrange();
26607         
26608         return;
26609     },
26610     
26611     arrange : function()
26612     {
26613         if(!this.delegates.length){
26614             this.progressDialog.hide();
26615             this.refresh();
26616             return;
26617         }
26618         
26619         var delegate = this.delegates.shift();
26620         
26621         this.progressDialog.show();
26622         
26623         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26624         
26625         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26626         
26627         delegate();
26628     },
26629     
26630     refresh : function()
26631     {
26632         this.uploader.show();
26633         
26634         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26635             this.uploader.hide();
26636         }
26637         
26638         Roo.isTouch ? this.closable(false) : this.closable(true);
26639         
26640         this.fireEvent('refresh', this);
26641     },
26642     
26643     onRemove : function(e, el, o)
26644     {
26645         e.preventDefault();
26646         
26647         this.fireEvent('remove', this, o);
26648         
26649     },
26650     
26651     remove : function(o)
26652     {
26653         var files = [];
26654         
26655         Roo.each(this.files, function(file){
26656             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26657                 files.push(file);
26658                 return;
26659             }
26660
26661             o.target.remove();
26662
26663         }, this);
26664         
26665         this.files = files;
26666         
26667         this.refresh();
26668     },
26669     
26670     clear : function()
26671     {
26672         Roo.each(this.files, function(file){
26673             if(!file.target){
26674                 return;
26675             }
26676             
26677             file.target.remove();
26678
26679         }, this);
26680         
26681         this.files = [];
26682         
26683         this.refresh();
26684     },
26685     
26686     onClick : function(e, el, o)
26687     {
26688         e.preventDefault();
26689         
26690         this.fireEvent('click', this, o);
26691         
26692     },
26693     
26694     closable : function(closable)
26695     {
26696         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26697             
26698             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26699             
26700             if(closable){
26701                 el.show();
26702                 return;
26703             }
26704             
26705             el.hide();
26706             
26707         }, this);
26708     },
26709     
26710     xhrOnLoad : function(xhr)
26711     {
26712         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26713             el.remove();
26714         }, this);
26715         
26716         if (xhr.readyState !== 4) {
26717             this.arrange();
26718             this.fireEvent('exception', this, xhr);
26719             return;
26720         }
26721
26722         var response = Roo.decode(xhr.responseText);
26723         
26724         if(!response.success){
26725             this.arrange();
26726             this.fireEvent('exception', this, xhr);
26727             return;
26728         }
26729         
26730         var file = this.renderPreview(response.data);
26731         
26732         this.files.push(file);
26733         
26734         this.arrange();
26735         
26736     },
26737     
26738     xhrOnError : function(xhr)
26739     {
26740         Roo.log('xhr on error');
26741         
26742         var response = Roo.decode(xhr.responseText);
26743           
26744         Roo.log(response);
26745         
26746         this.arrange();
26747     },
26748     
26749     process : function(file)
26750     {
26751         if(this.fireEvent('process', this, file) !== false){
26752             if(this.editable && file.type.indexOf('image') != -1){
26753                 this.fireEvent('edit', this, file);
26754                 return;
26755             }
26756
26757             this.uploadStart(file, false);
26758
26759             return;
26760         }
26761         
26762     },
26763     
26764     uploadStart : function(file, crop)
26765     {
26766         this.xhr = new XMLHttpRequest();
26767         
26768         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26769             this.arrange();
26770             return;
26771         }
26772         
26773         file.xhr = this.xhr;
26774             
26775         this.managerEl.createChild({
26776             tag : 'div',
26777             cls : 'roo-document-manager-loading',
26778             cn : [
26779                 {
26780                     tag : 'div',
26781                     tooltip : file.name,
26782                     cls : 'roo-document-manager-thumb',
26783                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26784                 }
26785             ]
26786
26787         });
26788
26789         this.xhr.open(this.method, this.url, true);
26790         
26791         var headers = {
26792             "Accept": "application/json",
26793             "Cache-Control": "no-cache",
26794             "X-Requested-With": "XMLHttpRequest"
26795         };
26796         
26797         for (var headerName in headers) {
26798             var headerValue = headers[headerName];
26799             if (headerValue) {
26800                 this.xhr.setRequestHeader(headerName, headerValue);
26801             }
26802         }
26803         
26804         var _this = this;
26805         
26806         this.xhr.onload = function()
26807         {
26808             _this.xhrOnLoad(_this.xhr);
26809         }
26810         
26811         this.xhr.onerror = function()
26812         {
26813             _this.xhrOnError(_this.xhr);
26814         }
26815         
26816         var formData = new FormData();
26817
26818         formData.append('returnHTML', 'NO');
26819         
26820         if(crop){
26821             formData.append('crop', crop);
26822         }
26823         
26824         formData.append(this.paramName, file, file.name);
26825         
26826         if(this.fireEvent('prepare', this, formData) != false){
26827             this.xhr.send(formData);
26828         };
26829     },
26830     
26831     uploadCancel : function()
26832     {
26833         if (this.xhr) {
26834             this.xhr.abort();
26835         }
26836         
26837         
26838         this.delegates = [];
26839         
26840         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26841             el.remove();
26842         }, this);
26843         
26844         this.arrange();
26845     },
26846     
26847     renderPreview : function(file)
26848     {
26849         if(typeof(file.target) != 'undefined' && file.target){
26850             return file;
26851         }
26852         
26853         var previewEl = this.managerEl.createChild({
26854             tag : 'div',
26855             cls : 'roo-document-manager-preview',
26856             cn : [
26857                 {
26858                     tag : 'div',
26859                     tooltip : file.filename,
26860                     cls : 'roo-document-manager-thumb',
26861                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26862                 },
26863                 {
26864                     tag : 'button',
26865                     cls : 'close',
26866                     html : '<i class="fa fa-times-circle"></i>'
26867                 }
26868             ]
26869         });
26870
26871         var close = previewEl.select('button.close', true).first();
26872
26873         close.on('click', this.onRemove, this, file);
26874
26875         file.target = previewEl;
26876
26877         var image = previewEl.select('img', true).first();
26878         
26879         var _this = this;
26880         
26881         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26882         
26883         image.on('click', this.onClick, this, file);
26884         
26885         return file;
26886         
26887     },
26888     
26889     onPreviewLoad : function(file, image)
26890     {
26891         if(typeof(file.target) == 'undefined' || !file.target){
26892             return;
26893         }
26894         
26895         var width = image.dom.naturalWidth || image.dom.width;
26896         var height = image.dom.naturalHeight || image.dom.height;
26897         
26898         if(width > height){
26899             file.target.addClass('wide');
26900             return;
26901         }
26902         
26903         file.target.addClass('tall');
26904         return;
26905         
26906     },
26907     
26908     uploadFromSource : function(file, crop)
26909     {
26910         this.xhr = new XMLHttpRequest();
26911         
26912         this.managerEl.createChild({
26913             tag : 'div',
26914             cls : 'roo-document-manager-loading',
26915             cn : [
26916                 {
26917                     tag : 'div',
26918                     tooltip : file.name,
26919                     cls : 'roo-document-manager-thumb',
26920                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26921                 }
26922             ]
26923
26924         });
26925
26926         this.xhr.open(this.method, this.url, true);
26927         
26928         var headers = {
26929             "Accept": "application/json",
26930             "Cache-Control": "no-cache",
26931             "X-Requested-With": "XMLHttpRequest"
26932         };
26933         
26934         for (var headerName in headers) {
26935             var headerValue = headers[headerName];
26936             if (headerValue) {
26937                 this.xhr.setRequestHeader(headerName, headerValue);
26938             }
26939         }
26940         
26941         var _this = this;
26942         
26943         this.xhr.onload = function()
26944         {
26945             _this.xhrOnLoad(_this.xhr);
26946         }
26947         
26948         this.xhr.onerror = function()
26949         {
26950             _this.xhrOnError(_this.xhr);
26951         }
26952         
26953         var formData = new FormData();
26954
26955         formData.append('returnHTML', 'NO');
26956         
26957         formData.append('crop', crop);
26958         
26959         if(typeof(file.filename) != 'undefined'){
26960             formData.append('filename', file.filename);
26961         }
26962         
26963         if(typeof(file.mimetype) != 'undefined'){
26964             formData.append('mimetype', file.mimetype);
26965         }
26966         
26967         if(this.fireEvent('prepare', this, formData) != false){
26968             this.xhr.send(formData);
26969         };
26970     }
26971 });
26972
26973 /*
26974 * Licence: LGPL
26975 */
26976
26977 /**
26978  * @class Roo.bootstrap.DocumentViewer
26979  * @extends Roo.bootstrap.Component
26980  * Bootstrap DocumentViewer class
26981  * 
26982  * @constructor
26983  * Create a new DocumentViewer
26984  * @param {Object} config The config object
26985  */
26986
26987 Roo.bootstrap.DocumentViewer = function(config){
26988     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26989     
26990     this.addEvents({
26991         /**
26992          * @event initial
26993          * Fire after initEvent
26994          * @param {Roo.bootstrap.DocumentViewer} this
26995          */
26996         "initial" : true,
26997         /**
26998          * @event click
26999          * Fire after click
27000          * @param {Roo.bootstrap.DocumentViewer} this
27001          */
27002         "click" : true,
27003         /**
27004          * @event trash
27005          * Fire after trash button
27006          * @param {Roo.bootstrap.DocumentViewer} this
27007          */
27008         "trash" : true
27009         
27010     });
27011 };
27012
27013 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27014     
27015     getAutoCreate : function()
27016     {
27017         var cfg = {
27018             tag : 'div',
27019             cls : 'roo-document-viewer',
27020             cn : [
27021                 {
27022                     tag : 'div',
27023                     cls : 'roo-document-viewer-body',
27024                     cn : [
27025                         {
27026                             tag : 'div',
27027                             cls : 'roo-document-viewer-thumb',
27028                             cn : [
27029                                 {
27030                                     tag : 'img',
27031                                     cls : 'roo-document-viewer-image'
27032                                 }
27033                             ]
27034                         }
27035                     ]
27036                 },
27037                 {
27038                     tag : 'div',
27039                     cls : 'roo-document-viewer-footer',
27040                     cn : {
27041                         tag : 'div',
27042                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27043                         cn : [
27044                             {
27045                                 tag : 'div',
27046                                 cls : 'btn-group',
27047                                 cn : [
27048                                     {
27049                                         tag : 'button',
27050                                         cls : 'btn btn-default roo-document-viewer-trash',
27051                                         html : '<i class="fa fa-trash"></i>'
27052                                     }
27053                                 ]
27054                             }
27055                         ]
27056                     }
27057                 }
27058             ]
27059         };
27060         
27061         return cfg;
27062     },
27063     
27064     initEvents : function()
27065     {
27066         
27067         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27068         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27069         
27070         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27071         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27072         
27073         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27074         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27075         
27076         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27077         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27078         
27079         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27080         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27081         
27082         this.bodyEl.on('click', this.onClick, this);
27083         
27084         this.trashBtn.on('click', this.onTrash, this);
27085         
27086     },
27087     
27088     initial : function()
27089     {
27090 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27091         
27092         
27093         this.fireEvent('initial', this);
27094         
27095     },
27096     
27097     onClick : function(e)
27098     {
27099         e.preventDefault();
27100         
27101         this.fireEvent('click', this);
27102     },
27103     
27104     onTrash : function(e)
27105     {
27106         e.preventDefault();
27107         
27108         this.fireEvent('trash', this);
27109     }
27110     
27111 });
27112 /*
27113  * - LGPL
27114  *
27115  * nav progress bar
27116  * 
27117  */
27118
27119 /**
27120  * @class Roo.bootstrap.NavProgressBar
27121  * @extends Roo.bootstrap.Component
27122  * Bootstrap NavProgressBar class
27123  * 
27124  * @constructor
27125  * Create a new nav progress bar
27126  * @param {Object} config The config object
27127  */
27128
27129 Roo.bootstrap.NavProgressBar = function(config){
27130     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27131
27132     this.bullets = this.bullets || [];
27133    
27134 //    Roo.bootstrap.NavProgressBar.register(this);
27135      this.addEvents({
27136         /**
27137              * @event changed
27138              * Fires when the active item changes
27139              * @param {Roo.bootstrap.NavProgressBar} this
27140              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27141              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27142          */
27143         'changed': true
27144      });
27145     
27146 };
27147
27148 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27149     
27150     bullets : [],
27151     barItems : [],
27152     
27153     getAutoCreate : function()
27154     {
27155         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27156         
27157         cfg = {
27158             tag : 'div',
27159             cls : 'roo-navigation-bar-group',
27160             cn : [
27161                 {
27162                     tag : 'div',
27163                     cls : 'roo-navigation-top-bar'
27164                 },
27165                 {
27166                     tag : 'div',
27167                     cls : 'roo-navigation-bullets-bar',
27168                     cn : [
27169                         {
27170                             tag : 'ul',
27171                             cls : 'roo-navigation-bar'
27172                         }
27173                     ]
27174                 },
27175                 
27176                 {
27177                     tag : 'div',
27178                     cls : 'roo-navigation-bottom-bar'
27179                 }
27180             ]
27181             
27182         };
27183         
27184         return cfg;
27185         
27186     },
27187     
27188     initEvents: function() 
27189     {
27190         
27191     },
27192     
27193     onRender : function(ct, position) 
27194     {
27195         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27196         
27197         if(this.bullets.length){
27198             Roo.each(this.bullets, function(b){
27199                this.addItem(b);
27200             }, this);
27201         }
27202         
27203         this.format();
27204         
27205     },
27206     
27207     addItem : function(cfg)
27208     {
27209         var item = new Roo.bootstrap.NavProgressItem(cfg);
27210         
27211         item.parentId = this.id;
27212         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27213         
27214         if(cfg.html){
27215             var top = new Roo.bootstrap.Element({
27216                 tag : 'div',
27217                 cls : 'roo-navigation-bar-text'
27218             });
27219             
27220             var bottom = new Roo.bootstrap.Element({
27221                 tag : 'div',
27222                 cls : 'roo-navigation-bar-text'
27223             });
27224             
27225             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27226             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27227             
27228             var topText = new Roo.bootstrap.Element({
27229                 tag : 'span',
27230                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27231             });
27232             
27233             var bottomText = new Roo.bootstrap.Element({
27234                 tag : 'span',
27235                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27236             });
27237             
27238             topText.onRender(top.el, null);
27239             bottomText.onRender(bottom.el, null);
27240             
27241             item.topEl = top;
27242             item.bottomEl = bottom;
27243         }
27244         
27245         this.barItems.push(item);
27246         
27247         return item;
27248     },
27249     
27250     getActive : function()
27251     {
27252         var active = false;
27253         
27254         Roo.each(this.barItems, function(v){
27255             
27256             if (!v.isActive()) {
27257                 return;
27258             }
27259             
27260             active = v;
27261             return false;
27262             
27263         });
27264         
27265         return active;
27266     },
27267     
27268     setActiveItem : function(item)
27269     {
27270         var prev = false;
27271         
27272         Roo.each(this.barItems, function(v){
27273             if (v.rid == item.rid) {
27274                 return ;
27275             }
27276             
27277             if (v.isActive()) {
27278                 v.setActive(false);
27279                 prev = v;
27280             }
27281         });
27282
27283         item.setActive(true);
27284         
27285         this.fireEvent('changed', this, item, prev);
27286     },
27287     
27288     getBarItem: function(rid)
27289     {
27290         var ret = false;
27291         
27292         Roo.each(this.barItems, function(e) {
27293             if (e.rid != rid) {
27294                 return;
27295             }
27296             
27297             ret =  e;
27298             return false;
27299         });
27300         
27301         return ret;
27302     },
27303     
27304     indexOfItem : function(item)
27305     {
27306         var index = false;
27307         
27308         Roo.each(this.barItems, function(v, i){
27309             
27310             if (v.rid != item.rid) {
27311                 return;
27312             }
27313             
27314             index = i;
27315             return false
27316         });
27317         
27318         return index;
27319     },
27320     
27321     setActiveNext : function()
27322     {
27323         var i = this.indexOfItem(this.getActive());
27324         
27325         if (i > this.barItems.length) {
27326             return;
27327         }
27328         
27329         this.setActiveItem(this.barItems[i+1]);
27330     },
27331     
27332     setActivePrev : function()
27333     {
27334         var i = this.indexOfItem(this.getActive());
27335         
27336         if (i  < 1) {
27337             return;
27338         }
27339         
27340         this.setActiveItem(this.barItems[i-1]);
27341     },
27342     
27343     format : function()
27344     {
27345         if(!this.barItems.length){
27346             return;
27347         }
27348      
27349         var width = 100 / this.barItems.length;
27350         
27351         Roo.each(this.barItems, function(i){
27352             i.el.setStyle('width', width + '%');
27353             i.topEl.el.setStyle('width', width + '%');
27354             i.bottomEl.el.setStyle('width', width + '%');
27355         }, this);
27356         
27357     }
27358     
27359 });
27360 /*
27361  * - LGPL
27362  *
27363  * Nav Progress Item
27364  * 
27365  */
27366
27367 /**
27368  * @class Roo.bootstrap.NavProgressItem
27369  * @extends Roo.bootstrap.Component
27370  * Bootstrap NavProgressItem class
27371  * @cfg {String} rid the reference id
27372  * @cfg {Boolean} active (true|false) Is item active default false
27373  * @cfg {Boolean} disabled (true|false) Is item active default false
27374  * @cfg {String} html
27375  * @cfg {String} position (top|bottom) text position default bottom
27376  * @cfg {String} icon show icon instead of number
27377  * 
27378  * @constructor
27379  * Create a new NavProgressItem
27380  * @param {Object} config The config object
27381  */
27382 Roo.bootstrap.NavProgressItem = function(config){
27383     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27384     this.addEvents({
27385         // raw events
27386         /**
27387          * @event click
27388          * The raw click event for the entire grid.
27389          * @param {Roo.bootstrap.NavProgressItem} this
27390          * @param {Roo.EventObject} e
27391          */
27392         "click" : true
27393     });
27394    
27395 };
27396
27397 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27398     
27399     rid : '',
27400     active : false,
27401     disabled : false,
27402     html : '',
27403     position : 'bottom',
27404     icon : false,
27405     
27406     getAutoCreate : function()
27407     {
27408         var iconCls = 'roo-navigation-bar-item-icon';
27409         
27410         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27411         
27412         var cfg = {
27413             tag: 'li',
27414             cls: 'roo-navigation-bar-item',
27415             cn : [
27416                 {
27417                     tag : 'i',
27418                     cls : iconCls
27419                 }
27420             ]
27421         };
27422         
27423         if(this.active){
27424             cfg.cls += ' active';
27425         }
27426         if(this.disabled){
27427             cfg.cls += ' disabled';
27428         }
27429         
27430         return cfg;
27431     },
27432     
27433     disable : function()
27434     {
27435         this.setDisabled(true);
27436     },
27437     
27438     enable : function()
27439     {
27440         this.setDisabled(false);
27441     },
27442     
27443     initEvents: function() 
27444     {
27445         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27446         
27447         this.iconEl.on('click', this.onClick, this);
27448     },
27449     
27450     onClick : function(e)
27451     {
27452         e.preventDefault();
27453         
27454         if(this.disabled){
27455             return;
27456         }
27457         
27458         if(this.fireEvent('click', this, e) === false){
27459             return;
27460         };
27461         
27462         this.parent().setActiveItem(this);
27463     },
27464     
27465     isActive: function () 
27466     {
27467         return this.active;
27468     },
27469     
27470     setActive : function(state)
27471     {
27472         if(this.active == state){
27473             return;
27474         }
27475         
27476         this.active = state;
27477         
27478         if (state) {
27479             this.el.addClass('active');
27480             return;
27481         }
27482         
27483         this.el.removeClass('active');
27484         
27485         return;
27486     },
27487     
27488     setDisabled : function(state)
27489     {
27490         if(this.disabled == state){
27491             return;
27492         }
27493         
27494         this.disabled = state;
27495         
27496         if (state) {
27497             this.el.addClass('disabled');
27498             return;
27499         }
27500         
27501         this.el.removeClass('disabled');
27502     },
27503     
27504     tooltipEl : function()
27505     {
27506         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27507     }
27508 });
27509  
27510
27511  /*
27512  * - LGPL
27513  *
27514  * FieldLabel
27515  * 
27516  */
27517
27518 /**
27519  * @class Roo.bootstrap.FieldLabel
27520  * @extends Roo.bootstrap.Component
27521  * Bootstrap FieldLabel class
27522  * @cfg {String} html contents of the element
27523  * @cfg {String} tag tag of the element default label
27524  * @cfg {String} cls class of the element
27525  * @cfg {String} target label target 
27526  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27527  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27528  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27529  * @cfg {String} iconTooltip default "This field is required"
27530  * 
27531  * @constructor
27532  * Create a new FieldLabel
27533  * @param {Object} config The config object
27534  */
27535
27536 Roo.bootstrap.FieldLabel = function(config){
27537     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27538     
27539     this.addEvents({
27540             /**
27541              * @event invalid
27542              * Fires after the field has been marked as invalid.
27543              * @param {Roo.form.FieldLabel} this
27544              * @param {String} msg The validation message
27545              */
27546             invalid : true,
27547             /**
27548              * @event valid
27549              * Fires after the field has been validated with no errors.
27550              * @param {Roo.form.FieldLabel} this
27551              */
27552             valid : true
27553         });
27554 };
27555
27556 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27557     
27558     tag: 'label',
27559     cls: '',
27560     html: '',
27561     target: '',
27562     allowBlank : true,
27563     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27564     validClass : 'text-success fa fa-lg fa-check',
27565     iconTooltip : 'This field is required',
27566     
27567     getAutoCreate : function(){
27568         
27569         var cfg = {
27570             tag : this.tag,
27571             cls : 'roo-bootstrap-field-label ' + this.cls,
27572             for : this.target,
27573             cn : [
27574                 {
27575                     tag : 'i',
27576                     cls : '',
27577                     tooltip : this.iconTooltip
27578                 },
27579                 {
27580                     tag : 'span',
27581                     html : this.html
27582                 }
27583             ] 
27584         };
27585         
27586         return cfg;
27587     },
27588     
27589     initEvents: function() 
27590     {
27591         Roo.bootstrap.Element.superclass.initEvents.call(this);
27592         
27593         this.iconEl = this.el.select('i', true).first();
27594         
27595         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27596         
27597         Roo.bootstrap.FieldLabel.register(this);
27598     },
27599     
27600     /**
27601      * Mark this field as valid
27602      */
27603     markValid : function()
27604     {
27605         this.iconEl.show();
27606         
27607         this.iconEl.removeClass(this.invalidClass);
27608         
27609         this.iconEl.addClass(this.validClass);
27610         
27611         this.fireEvent('valid', this);
27612     },
27613     
27614     /**
27615      * Mark this field as invalid
27616      * @param {String} msg The validation message
27617      */
27618     markInvalid : function(msg)
27619     {
27620         this.iconEl.show();
27621         
27622         this.iconEl.removeClass(this.validClass);
27623         
27624         this.iconEl.addClass(this.invalidClass);
27625         
27626         this.fireEvent('invalid', this, msg);
27627     }
27628     
27629    
27630 });
27631
27632 Roo.apply(Roo.bootstrap.FieldLabel, {
27633     
27634     groups: {},
27635     
27636      /**
27637     * register a FieldLabel Group
27638     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27639     */
27640     register : function(label)
27641     {
27642         if(this.groups.hasOwnProperty(label.target)){
27643             return;
27644         }
27645      
27646         this.groups[label.target] = label;
27647         
27648     },
27649     /**
27650     * fetch a FieldLabel Group based on the target
27651     * @param {string} target
27652     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27653     */
27654     get: function(target) {
27655         if (typeof(this.groups[target]) == 'undefined') {
27656             return false;
27657         }
27658         
27659         return this.groups[target] ;
27660     }
27661 });
27662
27663  
27664
27665  /*
27666  * - LGPL
27667  *
27668  * page DateSplitField.
27669  * 
27670  */
27671
27672
27673 /**
27674  * @class Roo.bootstrap.DateSplitField
27675  * @extends Roo.bootstrap.Component
27676  * Bootstrap DateSplitField class
27677  * @cfg {string} fieldLabel - the label associated
27678  * @cfg {Number} labelWidth set the width of label (0-12)
27679  * @cfg {String} labelAlign (top|left)
27680  * @cfg {Boolean} dayAllowBlank (true|false) default false
27681  * @cfg {Boolean} monthAllowBlank (true|false) default false
27682  * @cfg {Boolean} yearAllowBlank (true|false) default false
27683  * @cfg {string} dayPlaceholder 
27684  * @cfg {string} monthPlaceholder
27685  * @cfg {string} yearPlaceholder
27686  * @cfg {string} dayFormat default 'd'
27687  * @cfg {string} monthFormat default 'm'
27688  * @cfg {string} yearFormat default 'Y'
27689
27690  *     
27691  * @constructor
27692  * Create a new DateSplitField
27693  * @param {Object} config The config object
27694  */
27695
27696 Roo.bootstrap.DateSplitField = function(config){
27697     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27698     
27699     this.addEvents({
27700         // raw events
27701          /**
27702          * @event years
27703          * getting the data of years
27704          * @param {Roo.bootstrap.DateSplitField} this
27705          * @param {Object} years
27706          */
27707         "years" : true,
27708         /**
27709          * @event days
27710          * getting the data of days
27711          * @param {Roo.bootstrap.DateSplitField} this
27712          * @param {Object} days
27713          */
27714         "days" : true,
27715         /**
27716          * @event invalid
27717          * Fires after the field has been marked as invalid.
27718          * @param {Roo.form.Field} this
27719          * @param {String} msg The validation message
27720          */
27721         invalid : true,
27722        /**
27723          * @event valid
27724          * Fires after the field has been validated with no errors.
27725          * @param {Roo.form.Field} this
27726          */
27727         valid : true
27728     });
27729 };
27730
27731 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
27732     
27733     fieldLabel : '',
27734     labelAlign : 'top',
27735     labelWidth : 3,
27736     dayAllowBlank : false,
27737     monthAllowBlank : false,
27738     yearAllowBlank : false,
27739     dayPlaceholder : '',
27740     monthPlaceholder : '',
27741     yearPlaceholder : '',
27742     dayFormat : 'd',
27743     monthFormat : 'm',
27744     yearFormat : 'Y',
27745     isFormField : true,
27746     
27747     getAutoCreate : function()
27748     {
27749         var cfg = {
27750             tag : 'div',
27751             cls : 'row roo-date-split-field-group',
27752             cn : [
27753                 {
27754                     tag : 'input',
27755                     type : 'hidden',
27756                     cls : 'form-hidden-field roo-date-split-field-group-value',
27757                     name : this.name
27758                 }
27759             ]
27760         };
27761         
27762         if(this.fieldLabel){
27763             cfg.cn.push({
27764                 tag : 'div',
27765                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27766                 cn : [
27767                     {
27768                         tag : 'label',
27769                         html : this.fieldLabel
27770                     }
27771                 ]
27772             });
27773         }
27774         
27775         Roo.each(['day', 'month', 'year'], function(t){
27776             cfg.cn.push({
27777                 tag : 'div',
27778                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27779             });
27780         }, this);
27781         
27782         return cfg;
27783     },
27784     
27785     inputEl: function ()
27786     {
27787         return this.el.select('.roo-date-split-field-group-value', true).first();
27788     },
27789     
27790     onRender : function(ct, position) 
27791     {
27792         var _this = this;
27793         
27794         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27795         
27796         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27797         
27798         this.dayField = new Roo.bootstrap.ComboBox({
27799             allowBlank : this.dayAllowBlank,
27800             alwaysQuery : true,
27801             displayField : 'value',
27802             editable : false,
27803             fieldLabel : '',
27804             forceSelection : true,
27805             mode : 'local',
27806             placeholder : this.dayPlaceholder,
27807             selectOnFocus : true,
27808             tpl : '<div class="select2-result"><b>{value}</b></div>',
27809             triggerAction : 'all',
27810             typeAhead : true,
27811             valueField : 'value',
27812             store : new Roo.data.SimpleStore({
27813                 data : (function() {    
27814                     var days = [];
27815                     _this.fireEvent('days', _this, days);
27816                     return days;
27817                 })(),
27818                 fields : [ 'value' ]
27819             }),
27820             listeners : {
27821                 select : function (_self, record, index)
27822                 {
27823                     _this.setValue(_this.getValue());
27824                 }
27825             }
27826         });
27827
27828         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27829         
27830         this.monthField = new Roo.bootstrap.MonthField({
27831             after : '<i class=\"fa fa-calendar\"></i>',
27832             allowBlank : this.monthAllowBlank,
27833             placeholder : this.monthPlaceholder,
27834             readOnly : true,
27835             listeners : {
27836                 render : function (_self)
27837                 {
27838                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
27839                         e.preventDefault();
27840                         _self.focus();
27841                     });
27842                 },
27843                 select : function (_self, oldvalue, newvalue)
27844                 {
27845                     _this.setValue(_this.getValue());
27846                 }
27847             }
27848         });
27849         
27850         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27851         
27852         this.yearField = new Roo.bootstrap.ComboBox({
27853             allowBlank : this.yearAllowBlank,
27854             alwaysQuery : true,
27855             displayField : 'value',
27856             editable : false,
27857             fieldLabel : '',
27858             forceSelection : true,
27859             mode : 'local',
27860             placeholder : this.yearPlaceholder,
27861             selectOnFocus : true,
27862             tpl : '<div class="select2-result"><b>{value}</b></div>',
27863             triggerAction : 'all',
27864             typeAhead : true,
27865             valueField : 'value',
27866             store : new Roo.data.SimpleStore({
27867                 data : (function() {
27868                     var years = [];
27869                     _this.fireEvent('years', _this, years);
27870                     return years;
27871                 })(),
27872                 fields : [ 'value' ]
27873             }),
27874             listeners : {
27875                 select : function (_self, record, index)
27876                 {
27877                     _this.setValue(_this.getValue());
27878                 }
27879             }
27880         });
27881
27882         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27883     },
27884     
27885     setValue : function(v, format)
27886     {
27887         this.inputEl.dom.value = v;
27888         
27889         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27890         
27891         var d = Date.parseDate(v, f);
27892         
27893         if(!d){
27894             this.validate();
27895             return;
27896         }
27897         
27898         this.setDay(d.format(this.dayFormat));
27899         this.setMonth(d.format(this.monthFormat));
27900         this.setYear(d.format(this.yearFormat));
27901         
27902         this.validate();
27903         
27904         return;
27905     },
27906     
27907     setDay : function(v)
27908     {
27909         this.dayField.setValue(v);
27910         this.inputEl.dom.value = this.getValue();
27911         this.validate();
27912         return;
27913     },
27914     
27915     setMonth : function(v)
27916     {
27917         this.monthField.setValue(v, true);
27918         this.inputEl.dom.value = this.getValue();
27919         this.validate();
27920         return;
27921     },
27922     
27923     setYear : function(v)
27924     {
27925         this.yearField.setValue(v);
27926         this.inputEl.dom.value = this.getValue();
27927         this.validate();
27928         return;
27929     },
27930     
27931     getDay : function()
27932     {
27933         return this.dayField.getValue();
27934     },
27935     
27936     getMonth : function()
27937     {
27938         return this.monthField.getValue();
27939     },
27940     
27941     getYear : function()
27942     {
27943         return this.yearField.getValue();
27944     },
27945     
27946     getValue : function()
27947     {
27948         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27949         
27950         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27951         
27952         return date;
27953     },
27954     
27955     reset : function()
27956     {
27957         this.setDay('');
27958         this.setMonth('');
27959         this.setYear('');
27960         this.inputEl.dom.value = '';
27961         this.validate();
27962         return;
27963     },
27964     
27965     validate : function()
27966     {
27967         var d = this.dayField.validate();
27968         var m = this.monthField.validate();
27969         var y = this.yearField.validate();
27970         
27971         var valid = true;
27972         
27973         if(
27974                 (!this.dayAllowBlank && !d) ||
27975                 (!this.monthAllowBlank && !m) ||
27976                 (!this.yearAllowBlank && !y)
27977         ){
27978             valid = false;
27979         }
27980         
27981         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27982             return valid;
27983         }
27984         
27985         if(valid){
27986             this.markValid();
27987             return valid;
27988         }
27989         
27990         this.markInvalid();
27991         
27992         return valid;
27993     },
27994     
27995     markValid : function()
27996     {
27997         
27998         var label = this.el.select('label', true).first();
27999         var icon = this.el.select('i.fa-star', true).first();
28000
28001         if(label && icon){
28002             icon.remove();
28003         }
28004         
28005         this.fireEvent('valid', this);
28006     },
28007     
28008      /**
28009      * Mark this field as invalid
28010      * @param {String} msg The validation message
28011      */
28012     markInvalid : function(msg)
28013     {
28014         
28015         var label = this.el.select('label', true).first();
28016         var icon = this.el.select('i.fa-star', true).first();
28017
28018         if(label && !icon){
28019             this.el.select('.roo-date-split-field-label', true).createChild({
28020                 tag : 'i',
28021                 cls : 'text-danger fa fa-lg fa-star',
28022                 tooltip : 'This field is required',
28023                 style : 'margin-right:5px;'
28024             }, label, true);
28025         }
28026         
28027         this.fireEvent('invalid', this, msg);
28028     },
28029     
28030     clearInvalid : function()
28031     {
28032         var label = this.el.select('label', true).first();
28033         var icon = this.el.select('i.fa-star', true).first();
28034
28035         if(label && icon){
28036             icon.remove();
28037         }
28038         
28039         this.fireEvent('valid', this);
28040     },
28041     
28042     getName: function()
28043     {
28044         return this.name;
28045     }
28046     
28047 });
28048
28049