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         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         if (this.el) {
350             this.el.removeClass('hidden');
351         }
352     },
353     /**
354      * Hide a component - adds 'hidden' class
355      */
356     hide: function()
357     {
358         if (this.el && !this.el.hasClass('hidden')) {
359             this.el.addClass('hidden');
360         }
361         
362     }
363 });
364
365  /*
366  * - LGPL
367  *
368  * Body
369  * 
370  */
371
372 /**
373  * @class Roo.bootstrap.Body
374  * @extends Roo.bootstrap.Component
375  * Bootstrap Body class
376  * 
377  * @constructor
378  * Create a new body
379  * @param {Object} config The config object
380  */
381
382 Roo.bootstrap.Body = function(config){
383     Roo.bootstrap.Body.superclass.constructor.call(this, config);
384     this.el = Roo.get(document.body);
385     if (this.cls && this.cls.length) {
386         Roo.get(document.body).addClass(this.cls);
387     }
388 };
389
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
391       
392         autoCreate : {
393         cls: 'container'
394     },
395     onRender : function(ct, position)
396     {
397        /* Roo.log("Roo.bootstrap.Body - onRender");
398         if (this.cls && this.cls.length) {
399             Roo.get(document.body).addClass(this.cls);
400         }
401         // style??? xttr???
402         */
403     }
404     
405     
406  
407    
408 });
409
410  /*
411  * - LGPL
412  *
413  * button group
414  * 
415  */
416
417
418 /**
419  * @class Roo.bootstrap.ButtonGroup
420  * @extends Roo.bootstrap.Component
421  * Bootstrap ButtonGroup class
422  * @cfg {String} size lg | sm | xs (default empty normal)
423  * @cfg {String} align vertical | justified  (default none)
424  * @cfg {String} direction up | down (default down)
425  * @cfg {Boolean} toolbar false | true
426  * @cfg {Boolean} btn true | false
427  * 
428  * 
429  * @constructor
430  * Create a new Input
431  * @param {Object} config The config object
432  */
433
434 Roo.bootstrap.ButtonGroup = function(config){
435     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
436 };
437
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
439     
440     size: '',
441     align: '',
442     direction: '',
443     toolbar: false,
444     btn: true,
445
446     getAutoCreate : function(){
447         var cfg = {
448             cls: 'btn-group',
449             html : null
450         }
451         
452         cfg.html = this.html || cfg.html;
453         
454         if (this.toolbar) {
455             cfg = {
456                 cls: 'btn-toolbar',
457                 html: null
458             }
459             
460             return cfg;
461         }
462         
463         if (['vertical','justified'].indexOf(this.align)!==-1) {
464             cfg.cls = 'btn-group-' + this.align;
465             
466             if (this.align == 'justified') {
467                 console.log(this.items);
468             }
469         }
470         
471         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472             cfg.cls += ' btn-group-' + this.size;
473         }
474         
475         if (this.direction == 'up') {
476             cfg.cls += ' dropup' ;
477         }
478         
479         return cfg;
480     }
481    
482 });
483
484  /*
485  * - LGPL
486  *
487  * button
488  * 
489  */
490
491 /**
492  * @class Roo.bootstrap.Button
493  * @extends Roo.bootstrap.Component
494  * Bootstrap Button class
495  * @cfg {String} html The button content
496  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
497  * @cfg {String} size ( lg | sm | xs)
498  * @cfg {String} tag ( a | input | submit)
499  * @cfg {String} href empty or href
500  * @cfg {Boolean} disabled default false;
501  * @cfg {Boolean} isClose default false;
502  * @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)
503  * @cfg {String} badge text for badge
504  * @cfg {String} theme default 
505  * @cfg {Boolean} inverse 
506  * @cfg {Boolean} toggle 
507  * @cfg {String} ontext text for on toggle state
508  * @cfg {String} offtext text for off toggle state
509  * @cfg {Boolean} defaulton 
510  * @cfg {Boolean} preventDefault  default true
511  * @cfg {Boolean} removeClass remove the standard class..
512  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
513  * 
514  * @constructor
515  * Create a new button
516  * @param {Object} config The config object
517  */
518
519
520 Roo.bootstrap.Button = function(config){
521     Roo.bootstrap.Button.superclass.constructor.call(this, config);
522     this.addEvents({
523         // raw events
524         /**
525          * @event click
526          * When a butotn is pressed
527          * @param {Roo.bootstrap.Button} this
528          * @param {Roo.EventObject} e
529          */
530         "click" : true,
531          /**
532          * @event toggle
533          * After the button has been toggles
534          * @param {Roo.EventObject} e
535          * @param {boolean} pressed (also available as button.pressed)
536          */
537         "toggle" : true
538     });
539 };
540
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
542     html: false,
543     active: false,
544     weight: '',
545     size: '',
546     tag: 'button',
547     href: '',
548     disabled: false,
549     isClose: false,
550     glyphicon: '',
551     badge: '',
552     theme: 'default',
553     inverse: false,
554     
555     toggle: false,
556     ontext: 'ON',
557     offtext: 'OFF',
558     defaulton: true,
559     preventDefault: true,
560     removeClass: false,
561     name: false,
562     target: false,
563     
564     
565     pressed : null,
566      
567     
568     getAutoCreate : function(){
569         
570         var cfg = {
571             tag : 'button',
572             cls : 'roo-button',
573             html: ''
574         };
575         
576         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
578             this.tag = 'button';
579         } else {
580             cfg.tag = this.tag;
581         }
582         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
583         
584         if (this.toggle == true) {
585             cfg={
586                 tag: 'div',
587                 cls: 'slider-frame roo-button',
588                 cn: [
589                     {
590                         tag: 'span',
591                         'data-on-text':'ON',
592                         'data-off-text':'OFF',
593                         cls: 'slider-button',
594                         html: this.offtext
595                     }
596                 ]
597             };
598             
599             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600                 cfg.cls += ' '+this.weight;
601             }
602             
603             return cfg;
604         }
605         
606         if (this.isClose) {
607             cfg.cls += ' close';
608             
609             cfg["aria-hidden"] = true;
610             
611             cfg.html = "&times;";
612             
613             return cfg;
614         }
615         
616          
617         if (this.theme==='default') {
618             cfg.cls = 'btn roo-button';
619             
620             //if (this.parentType != 'Navbar') {
621             this.weight = this.weight.length ?  this.weight : 'default';
622             //}
623             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
624                 
625                 cfg.cls += ' btn-' + this.weight;
626             }
627         } else if (this.theme==='glow') {
628             
629             cfg.tag = 'a';
630             cfg.cls = 'btn-glow roo-button';
631             
632             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
633                 
634                 cfg.cls += ' ' + this.weight;
635             }
636         }
637    
638         
639         if (this.inverse) {
640             this.cls += ' inverse';
641         }
642         
643         
644         if (this.active) {
645             cfg.cls += ' active';
646         }
647         
648         if (this.disabled) {
649             cfg.disabled = 'disabled';
650         }
651         
652         if (this.items) {
653             Roo.log('changing to ul' );
654             cfg.tag = 'ul';
655             this.glyphicon = 'caret';
656         }
657         
658         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
659          
660         //gsRoo.log(this.parentType);
661         if (this.parentType === 'Navbar' && !this.parent().bar) {
662             Roo.log('changing to li?');
663             
664             cfg.tag = 'li';
665             
666             cfg.cls = '';
667             cfg.cn =  [{
668                 tag : 'a',
669                 cls : 'roo-button',
670                 html : this.html,
671                 href : this.href || '#'
672             }];
673             if (this.menu) {
674                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
675                 cfg.cls += ' dropdown';
676             }   
677             
678             delete cfg.html;
679             
680         }
681         
682        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
683         
684         if (this.glyphicon) {
685             cfg.html = ' ' + cfg.html;
686             
687             cfg.cn = [
688                 {
689                     tag: 'span',
690                     cls: 'glyphicon glyphicon-' + this.glyphicon
691                 }
692             ];
693         }
694         
695         if (this.badge) {
696             cfg.html += ' ';
697             
698             cfg.tag = 'a';
699             
700 //            cfg.cls='btn roo-button';
701             
702             cfg.href=this.href;
703             
704             var value = cfg.html;
705             
706             if(this.glyphicon){
707                 value = {
708                             tag: 'span',
709                             cls: 'glyphicon glyphicon-' + this.glyphicon,
710                             html: this.html
711                         };
712                 
713             }
714             
715             cfg.cn = [
716                 value,
717                 {
718                     tag: 'span',
719                     cls: 'badge',
720                     html: this.badge
721                 }
722             ];
723             
724             cfg.html='';
725         }
726         
727         if (this.menu) {
728             cfg.cls += ' dropdown';
729             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
730         }
731         
732         if (cfg.tag !== 'a' && this.href !== '') {
733             throw "Tag must be a to set href.";
734         } else if (this.href.length > 0) {
735             cfg.href = this.href;
736         }
737         
738         if(this.removeClass){
739             cfg.cls = '';
740         }
741         
742         if(this.target){
743             cfg.target = this.target;
744         }
745         
746         return cfg;
747     },
748     initEvents: function() {
749        // Roo.log('init events?');
750 //        Roo.log(this.el.dom);
751         // add the menu...
752         
753         if (typeof (this.menu) != 'undefined') {
754             this.menu.parentType = this.xtype;
755             this.menu.triggerEl = this.el;
756             this.addxtype(Roo.apply({}, this.menu));
757         }
758
759
760        if (this.el.hasClass('roo-button')) {
761             this.el.on('click', this.onClick, this);
762        } else {
763             this.el.select('.roo-button').on('click', this.onClick, this);
764        }
765        
766        if(this.removeClass){
767            this.el.on('click', this.onClick, this);
768        }
769        
770        this.el.enableDisplayMode();
771         
772     },
773     onClick : function(e)
774     {
775         if (this.disabled) {
776             return;
777         }
778         
779         
780         Roo.log('button on click ');
781         if(this.preventDefault){
782             e.preventDefault();
783         }
784         if (this.pressed === true || this.pressed === false) {
785             this.pressed = !this.pressed;
786             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787             this.fireEvent('toggle', this, e, this.pressed);
788         }
789         
790         
791         this.fireEvent('click', this, e);
792     },
793     
794     /**
795      * Enables this button
796      */
797     enable : function()
798     {
799         this.disabled = false;
800         this.el.removeClass('disabled');
801     },
802     
803     /**
804      * Disable this button
805      */
806     disable : function()
807     {
808         this.disabled = true;
809         this.el.addClass('disabled');
810     },
811      /**
812      * sets the active state on/off, 
813      * @param {Boolean} state (optional) Force a particular state
814      */
815     setActive : function(v) {
816         
817         this.el[v ? 'addClass' : 'removeClass']('active');
818     },
819      /**
820      * toggles the current active state 
821      */
822     toggleActive : function()
823     {
824        var active = this.el.hasClass('active');
825        this.setActive(!active);
826        
827         
828     },
829     setText : function(str)
830     {
831         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
832     },
833     getText : function()
834     {
835         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
836     },
837     hide: function() {
838        
839      
840         this.el.hide();   
841     },
842     show: function() {
843        
844         this.el.show();   
845     }
846     
847     
848 });
849
850  /*
851  * - LGPL
852  *
853  * column
854  * 
855  */
856
857 /**
858  * @class Roo.bootstrap.Column
859  * @extends Roo.bootstrap.Component
860  * Bootstrap Column class
861  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
869  *
870  * 
871  * @cfg {Boolean} hidden (true|false) hide the element
872  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873  * @cfg {String} fa (ban|check|...) font awesome icon
874  * @cfg {Number} fasize (1|2|....) font awsome size
875
876  * @cfg {String} icon (info-sign|check|...) glyphicon name
877
878  * @cfg {String} html content of column.
879  * 
880  * @constructor
881  * Create a new Column
882  * @param {Object} config The config object
883  */
884
885 Roo.bootstrap.Column = function(config){
886     Roo.bootstrap.Column.superclass.constructor.call(this, config);
887 };
888
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
890     
891     xs: false,
892     sm: false,
893     md: false,
894     lg: false,
895     xsoff: false,
896     smoff: false,
897     mdoff: false,
898     lgoff: false,
899     html: '',
900     offset: 0,
901     alert: false,
902     fa: false,
903     icon : false,
904     hidden : false,
905     fasize : 1,
906     
907     getAutoCreate : function(){
908         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
909         
910         cfg = {
911             tag: 'div',
912             cls: 'column'
913         };
914         
915         var settings=this;
916         ['xs','sm','md','lg'].map(function(size){
917             //Roo.log( size + ':' + settings[size]);
918             
919             if (settings[size+'off'] !== false) {
920                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
921             }
922             
923             if (settings[size] === false) {
924                 return;
925             }
926             Roo.log(settings[size]);
927             if (!settings[size]) { // 0 = hidden
928                 cfg.cls += ' hidden-' + size;
929                 return;
930             }
931             cfg.cls += ' col-' + size + '-' + settings[size];
932             
933         });
934         
935         if (this.hidden) {
936             cfg.cls += ' hidden';
937         }
938         
939         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940             cfg.cls +=' alert alert-' + this.alert;
941         }
942         
943         
944         if (this.html.length) {
945             cfg.html = this.html;
946         }
947         if (this.fa) {
948             var fasize = '';
949             if (this.fasize > 1) {
950                 fasize = ' fa-' + this.fasize + 'x';
951             }
952             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
953             
954             
955         }
956         if (this.icon) {
957             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
958         }
959         
960         return cfg;
961     }
962    
963 });
964
965  
966
967  /*
968  * - LGPL
969  *
970  * page container.
971  * 
972  */
973
974
975 /**
976  * @class Roo.bootstrap.Container
977  * @extends Roo.bootstrap.Component
978  * Bootstrap Container class
979  * @cfg {Boolean} jumbotron is it a jumbotron element
980  * @cfg {String} html content of element
981  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983  * @cfg {String} header content of header (for panel)
984  * @cfg {String} footer content of footer (for panel)
985  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986  * @cfg {String} tag (header|aside|section) type of HTML tag.
987  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988  * @cfg {String} fa (ban|check|...) font awesome icon
989  * @cfg {String} icon (info-sign|check|...) glyphicon name
990  * @cfg {Boolean} hidden (true|false) hide the element
991  * @cfg {Boolean} expandable (true|false) default false
992  * @cfg {Boolean} expanded (true|false) default true
993  * @cfg {String} rheader contet on the right of header
994
995  *     
996  * @constructor
997  * Create a new Container
998  * @param {Object} config The config object
999  */
1000
1001 Roo.bootstrap.Container = function(config){
1002     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1003     
1004     this.addEvents({
1005         // raw events
1006          /**
1007          * @event expand
1008          * After the panel has been expand
1009          * 
1010          * @param {Roo.bootstrap.Container} this
1011          */
1012         "expand" : true,
1013         /**
1014          * @event collapse
1015          * After the panel has been collapsed
1016          * 
1017          * @param {Roo.bootstrap.Container} this
1018          */
1019         "collapse" : true
1020     });
1021 };
1022
1023 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1024     
1025     jumbotron : false,
1026     well: '',
1027     panel : '',
1028     header: '',
1029     footer : '',
1030     sticky: '',
1031     tag : false,
1032     alert : false,
1033     fa: false,
1034     icon : false,
1035     expandable : false,
1036     rheader : '',
1037     expanded : true,
1038   
1039      
1040     getChildContainer : function() {
1041         
1042         if(!this.el){
1043             return false;
1044         }
1045         
1046         if (this.panel.length) {
1047             return this.el.select('.panel-body',true).first();
1048         }
1049         
1050         return this.el;
1051     },
1052     
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : this.tag || 'div',
1058             html : '',
1059             cls : ''
1060         };
1061         if (this.jumbotron) {
1062             cfg.cls = 'jumbotron';
1063         }
1064         
1065         
1066         
1067         // - this is applied by the parent..
1068         //if (this.cls) {
1069         //    cfg.cls = this.cls + '';
1070         //}
1071         
1072         if (this.sticky.length) {
1073             
1074             var bd = Roo.get(document.body);
1075             if (!bd.hasClass('bootstrap-sticky')) {
1076                 bd.addClass('bootstrap-sticky');
1077                 Roo.select('html',true).setStyle('height', '100%');
1078             }
1079              
1080             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1081         }
1082         
1083         
1084         if (this.well.length) {
1085             switch (this.well) {
1086                 case 'lg':
1087                 case 'sm':
1088                     cfg.cls +=' well well-' +this.well;
1089                     break;
1090                 default:
1091                     cfg.cls +=' well';
1092                     break;
1093             }
1094         }
1095         
1096         if (this.hidden) {
1097             cfg.cls += ' hidden';
1098         }
1099         
1100         
1101         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102             cfg.cls +=' alert alert-' + this.alert;
1103         }
1104         
1105         var body = cfg;
1106         
1107         if (this.panel.length) {
1108             cfg.cls += ' panel panel-' + this.panel;
1109             cfg.cn = [];
1110             if (this.header.length) {
1111                 
1112                 var h = [];
1113                 
1114                 if(this.expandable){
1115                     
1116                     cfg.cls = cfg.cls + ' expandable';
1117                     
1118                     h.push({
1119                         tag: 'i',
1120                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1121                     });
1122                     
1123                 }
1124                 
1125                 h.push(
1126                     {
1127                         tag: 'span',
1128                         cls : 'panel-title',
1129                         html : (this.expandable ? '&nbsp;' : '') + this.header
1130                     },
1131                     {
1132                         tag: 'span',
1133                         cls: 'panel-header-right',
1134                         html: this.rheader
1135                     }
1136                 );
1137                 
1138                 cfg.cn.push({
1139                     cls : 'panel-heading',
1140                     style : this.expandable ? 'cursor: pointer' : '',
1141                     cn : h
1142                 });
1143                 
1144             }
1145             
1146             body = false;
1147             cfg.cn.push({
1148                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1149                 html : this.html
1150             });
1151             
1152             
1153             if (this.footer.length) {
1154                 cfg.cn.push({
1155                     cls : 'panel-footer',
1156                     html : this.footer
1157                     
1158                 });
1159             }
1160             
1161         }
1162         
1163         if (body) {
1164             body.html = this.html || cfg.html;
1165             // prefix with the icons..
1166             if (this.fa) {
1167                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1168             }
1169             if (this.icon) {
1170                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1171             }
1172             
1173             
1174         }
1175         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1176             cfg.cls =  'container';
1177         }
1178         
1179         return cfg;
1180     },
1181     
1182     initEvents: function() 
1183     {
1184         if(!this.expandable){
1185             return;
1186         }
1187         
1188         var headerEl = this.headerEl();
1189         
1190         if(!headerEl){
1191             return;
1192         }
1193         
1194         headerEl.on('click', this.onToggleClick, this);
1195         
1196     },
1197     
1198     onToggleClick : function()
1199     {
1200         var headerEl = this.headerEl();
1201         
1202         if(!headerEl){
1203             return;
1204         }
1205         
1206         if(this.expanded){
1207             this.collapse();
1208             return;
1209         }
1210         
1211         this.expand();
1212     },
1213     
1214     expand : function()
1215     {
1216         if(this.fireEvent('expand', this)) {
1217             
1218             this.expanded = true;
1219             
1220             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1221             
1222             this.el.select('.panel-body',true).first().removeClass('hide');
1223             
1224             var toggleEl = this.toggleEl();
1225
1226             if(!toggleEl){
1227                 return;
1228             }
1229
1230             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1231         }
1232         
1233     },
1234     
1235     collapse : function()
1236     {
1237         if(this.fireEvent('collapse', this)) {
1238             
1239             this.expanded = false;
1240             
1241             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1242             this.el.select('.panel-body',true).first().addClass('hide');
1243         
1244             var toggleEl = this.toggleEl();
1245
1246             if(!toggleEl){
1247                 return;
1248             }
1249
1250             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1251         }
1252     },
1253     
1254     toggleEl : function()
1255     {
1256         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1257             return;
1258         }
1259         
1260         return this.el.select('.panel-heading .fa',true).first();
1261     },
1262     
1263     headerEl : function()
1264     {
1265         if(!this.el || !this.panel.length || !this.header.length){
1266             return;
1267         }
1268         
1269         return this.el.select('.panel-heading',true).first()
1270     },
1271     
1272     titleEl : function()
1273     {
1274         if(!this.el || !this.panel.length || !this.header.length){
1275             return;
1276         }
1277         
1278         return this.el.select('.panel-title',true).first();
1279     },
1280     
1281     setTitle : function(v)
1282     {
1283         var titleEl = this.titleEl();
1284         
1285         if(!titleEl){
1286             return;
1287         }
1288         
1289         titleEl.dom.innerHTML = v;
1290     },
1291     
1292     getTitle : function()
1293     {
1294         
1295         var titleEl = this.titleEl();
1296         
1297         if(!titleEl){
1298             return '';
1299         }
1300         
1301         return titleEl.dom.innerHTML;
1302     },
1303     
1304     setRightTitle : function(v)
1305     {
1306         var t = this.el.select('.panel-header-right',true).first();
1307         
1308         if(!t){
1309             return;
1310         }
1311         
1312         t.dom.innerHTML = v;
1313     }
1314    
1315 });
1316
1317  /*
1318  * - LGPL
1319  *
1320  * image
1321  * 
1322  */
1323
1324
1325 /**
1326  * @class Roo.bootstrap.Img
1327  * @extends Roo.bootstrap.Component
1328  * Bootstrap Img class
1329  * @cfg {Boolean} imgResponsive false | true
1330  * @cfg {String} border rounded | circle | thumbnail
1331  * @cfg {String} src image source
1332  * @cfg {String} alt image alternative text
1333  * @cfg {String} href a tag href
1334  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1335  * @cfg {String} xsUrl xs image source
1336  * @cfg {String} smUrl sm image source
1337  * @cfg {String} mdUrl md image source
1338  * @cfg {String} lgUrl lg image source
1339  * 
1340  * @constructor
1341  * Create a new Input
1342  * @param {Object} config The config object
1343  */
1344
1345 Roo.bootstrap.Img = function(config){
1346     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1347     
1348     this.addEvents({
1349         // img events
1350         /**
1351          * @event click
1352          * The img click event for the img.
1353          * @param {Roo.EventObject} e
1354          */
1355         "click" : true
1356     });
1357 };
1358
1359 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1360     
1361     imgResponsive: true,
1362     border: '',
1363     src: '',
1364     href: false,
1365     target: false,
1366     xsUrl: '',
1367     smUrl: '',
1368     mdUrl: '',
1369     lgUrl: '',
1370
1371     getAutoCreate : function()
1372     {   
1373         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374             return this.createSingleImg();
1375         }
1376         
1377         var cfg = {
1378             tag: 'div',
1379             cls: 'roo-image-responsive-group',
1380             cn: []
1381         }
1382         var _this = this;
1383         
1384         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1385             
1386             if(!_this[size + 'Url']){
1387                 return;
1388             }
1389             
1390             var img = {
1391                 tag: 'img',
1392                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393                 html: _this.html || cfg.html,
1394                 src: _this[size + 'Url']
1395             }
1396             
1397             img.cls += ' roo-image-responsive-' + size;
1398             
1399             var s = ['xs', 'sm', 'md', 'lg'];
1400             
1401             s.splice(s.indexOf(size), 1);
1402             
1403             Roo.each(s, function(ss){
1404                 img.cls += ' hidden-' + ss;
1405             });
1406             
1407             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408                 cfg.cls += ' img-' + _this.border;
1409             }
1410             
1411             if(_this.alt){
1412                 cfg.alt = _this.alt;
1413             }
1414             
1415             if(_this.href){
1416                 var a = {
1417                     tag: 'a',
1418                     href: _this.href,
1419                     cn: [
1420                         img
1421                     ]
1422                 }
1423
1424                 if(this.target){
1425                     a.target = _this.target;
1426                 }
1427             }
1428             
1429             cfg.cn.push((_this.href) ? a : img);
1430             
1431         });
1432         
1433         return cfg;
1434     },
1435     
1436     createSingleImg : function()
1437     {
1438         var cfg = {
1439             tag: 'img',
1440             cls: (this.imgResponsive) ? 'img-responsive' : '',
1441             html : null
1442         }
1443         
1444         cfg.html = this.html || cfg.html;
1445         
1446         cfg.src = this.src || cfg.src;
1447         
1448         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449             cfg.cls += ' img-' + this.border;
1450         }
1451         
1452         if(this.alt){
1453             cfg.alt = this.alt;
1454         }
1455         
1456         if(this.href){
1457             var a = {
1458                 tag: 'a',
1459                 href: this.href,
1460                 cn: [
1461                     cfg
1462                 ]
1463             }
1464             
1465             if(this.target){
1466                 a.target = this.target;
1467             }
1468             
1469         }
1470         
1471         return (this.href) ? a : cfg;
1472     },
1473     
1474     initEvents: function() 
1475     {
1476         if(!this.href){
1477             this.el.on('click', this.onClick, this);
1478         }
1479         
1480     },
1481     
1482     onClick : function(e)
1483     {
1484         Roo.log('img onclick');
1485         this.fireEvent('click', this, e);
1486     }
1487    
1488 });
1489
1490  /*
1491  * - LGPL
1492  *
1493  * image
1494  * 
1495  */
1496
1497
1498 /**
1499  * @class Roo.bootstrap.Link
1500  * @extends Roo.bootstrap.Component
1501  * Bootstrap Link Class
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505  * @cfg {String} html the content of the link.
1506  * @cfg {String} anchor name for the anchor link
1507
1508  * @cfg {Boolean} preventDefault (true | false) default false
1509
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Link = function(config){
1517     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1531     
1532     href: false,
1533     target: false,
1534     preventDefault: false,
1535     anchor : false,
1536     alt : false,
1537
1538     getAutoCreate : function()
1539     {
1540         
1541         var cfg = {
1542             tag: 'a'
1543         };
1544         // anchor's do not require html/href...
1545         if (this.anchor === false) {
1546             cfg.html = this.html || '';
1547             cfg.href = this.href || '#';
1548         } else {
1549             cfg.name = this.anchor;
1550             if (this.html !== false) {
1551                 cfg.html = this.html;
1552             }
1553             if (this.href !== false) {
1554                 cfg.href = this.href;
1555             }
1556         }
1557         
1558         if(this.alt !== false){
1559             cfg.alt = this.alt;
1560         }
1561         
1562         
1563         if(this.target !== false) {
1564             cfg.target = this.target;
1565         }
1566         
1567         return cfg;
1568     },
1569     
1570     initEvents: function() {
1571         
1572         if(!this.href || this.preventDefault){
1573             this.el.on('click', this.onClick, this);
1574         }
1575     },
1576     
1577     onClick : function(e)
1578     {
1579         if(this.preventDefault){
1580             e.preventDefault();
1581         }
1582         //Roo.log('img onclick');
1583         this.fireEvent('click', this, e);
1584     }
1585    
1586 });
1587
1588  /*
1589  * - LGPL
1590  *
1591  * header
1592  * 
1593  */
1594
1595 /**
1596  * @class Roo.bootstrap.Header
1597  * @extends Roo.bootstrap.Component
1598  * Bootstrap Header class
1599  * @cfg {String} html content of header
1600  * @cfg {Number} level (1|2|3|4|5|6) default 1
1601  * 
1602  * @constructor
1603  * Create a new Header
1604  * @param {Object} config The config object
1605  */
1606
1607
1608 Roo.bootstrap.Header  = function(config){
1609     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1610 };
1611
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1613     
1614     //href : false,
1615     html : false,
1616     level : 1,
1617     
1618     
1619     
1620     getAutoCreate : function(){
1621         
1622         
1623         
1624         var cfg = {
1625             tag: 'h' + (1 *this.level),
1626             html: this.html || ''
1627         } ;
1628         
1629         return cfg;
1630     }
1631    
1632 });
1633
1634  
1635
1636  /*
1637  * Based on:
1638  * Ext JS Library 1.1.1
1639  * Copyright(c) 2006-2007, Ext JS, LLC.
1640  *
1641  * Originally Released Under LGPL - original licence link has changed is not relivant.
1642  *
1643  * Fork - LGPL
1644  * <script type="text/javascript">
1645  */
1646  
1647 /**
1648  * @class Roo.bootstrap.MenuMgr
1649  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1650  * @singleton
1651  */
1652 Roo.bootstrap.MenuMgr = function(){
1653    var menus, active, groups = {}, attached = false, lastShow = new Date();
1654
1655    // private - called when first menu is created
1656    function init(){
1657        menus = {};
1658        active = new Roo.util.MixedCollection();
1659        Roo.get(document).addKeyListener(27, function(){
1660            if(active.length > 0){
1661                hideAll();
1662            }
1663        });
1664    }
1665
1666    // private
1667    function hideAll(){
1668        if(active && active.length > 0){
1669            var c = active.clone();
1670            c.each(function(m){
1671                m.hide();
1672            });
1673        }
1674    }
1675
1676    // private
1677    function onHide(m){
1678        active.remove(m);
1679        if(active.length < 1){
1680            Roo.get(document).un("mouseup", onMouseDown);
1681             
1682            attached = false;
1683        }
1684    }
1685
1686    // private
1687    function onShow(m){
1688        var last = active.last();
1689        lastShow = new Date();
1690        active.add(m);
1691        if(!attached){
1692           Roo.get(document).on("mouseup", onMouseDown);
1693            
1694            attached = true;
1695        }
1696        if(m.parentMenu){
1697           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698           m.parentMenu.activeChild = m;
1699        }else if(last && last.isVisible()){
1700           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1701        }
1702    }
1703
1704    // private
1705    function onBeforeHide(m){
1706        if(m.activeChild){
1707            m.activeChild.hide();
1708        }
1709        if(m.autoHideTimer){
1710            clearTimeout(m.autoHideTimer);
1711            delete m.autoHideTimer;
1712        }
1713    }
1714
1715    // private
1716    function onBeforeShow(m){
1717        var pm = m.parentMenu;
1718        if(!pm && !m.allowOtherMenus){
1719            hideAll();
1720        }else if(pm && pm.activeChild && active != m){
1721            pm.activeChild.hide();
1722        }
1723    }
1724
1725    // private this should really trigger on mouseup..
1726    function onMouseDown(e){
1727         Roo.log("on Mouse Up");
1728         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1729             Roo.log("hideAll");
1730             hideAll();
1731             e.stopEvent();
1732         }
1733         
1734         
1735    }
1736
1737    // private
1738    function onBeforeCheck(mi, state){
1739        if(state){
1740            var g = groups[mi.group];
1741            for(var i = 0, l = g.length; i < l; i++){
1742                if(g[i] != mi){
1743                    g[i].setChecked(false);
1744                }
1745            }
1746        }
1747    }
1748
1749    return {
1750
1751        /**
1752         * Hides all menus that are currently visible
1753         */
1754        hideAll : function(){
1755             hideAll();  
1756        },
1757
1758        // private
1759        register : function(menu){
1760            if(!menus){
1761                init();
1762            }
1763            menus[menu.id] = menu;
1764            menu.on("beforehide", onBeforeHide);
1765            menu.on("hide", onHide);
1766            menu.on("beforeshow", onBeforeShow);
1767            menu.on("show", onShow);
1768            var g = menu.group;
1769            if(g && menu.events["checkchange"]){
1770                if(!groups[g]){
1771                    groups[g] = [];
1772                }
1773                groups[g].push(menu);
1774                menu.on("checkchange", onCheck);
1775            }
1776        },
1777
1778         /**
1779          * Returns a {@link Roo.menu.Menu} object
1780          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781          * be used to generate and return a new Menu instance.
1782          */
1783        get : function(menu){
1784            if(typeof menu == "string"){ // menu id
1785                return menus[menu];
1786            }else if(menu.events){  // menu instance
1787                return menu;
1788            }
1789            /*else if(typeof menu.length == 'number'){ // array of menu items?
1790                return new Roo.bootstrap.Menu({items:menu});
1791            }else{ // otherwise, must be a config
1792                return new Roo.bootstrap.Menu(menu);
1793            }
1794            */
1795            return false;
1796        },
1797
1798        // private
1799        unregister : function(menu){
1800            delete menus[menu.id];
1801            menu.un("beforehide", onBeforeHide);
1802            menu.un("hide", onHide);
1803            menu.un("beforeshow", onBeforeShow);
1804            menu.un("show", onShow);
1805            var g = menu.group;
1806            if(g && menu.events["checkchange"]){
1807                groups[g].remove(menu);
1808                menu.un("checkchange", onCheck);
1809            }
1810        },
1811
1812        // private
1813        registerCheckable : function(menuItem){
1814            var g = menuItem.group;
1815            if(g){
1816                if(!groups[g]){
1817                    groups[g] = [];
1818                }
1819                groups[g].push(menuItem);
1820                menuItem.on("beforecheckchange", onBeforeCheck);
1821            }
1822        },
1823
1824        // private
1825        unregisterCheckable : function(menuItem){
1826            var g = menuItem.group;
1827            if(g){
1828                groups[g].remove(menuItem);
1829                menuItem.un("beforecheckchange", onBeforeCheck);
1830            }
1831        }
1832    };
1833 }();/*
1834  * - LGPL
1835  *
1836  * menu
1837  * 
1838  */
1839
1840 /**
1841  * @class Roo.bootstrap.Menu
1842  * @extends Roo.bootstrap.Component
1843  * Bootstrap Menu class - container for MenuItems
1844  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1845  * 
1846  * @constructor
1847  * Create a new Menu
1848  * @param {Object} config The config object
1849  */
1850
1851
1852 Roo.bootstrap.Menu = function(config){
1853     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854     if (this.registerMenu) {
1855         Roo.bootstrap.MenuMgr.register(this);
1856     }
1857     this.addEvents({
1858         /**
1859          * @event beforeshow
1860          * Fires before this menu is displayed
1861          * @param {Roo.menu.Menu} this
1862          */
1863         beforeshow : true,
1864         /**
1865          * @event beforehide
1866          * Fires before this menu is hidden
1867          * @param {Roo.menu.Menu} this
1868          */
1869         beforehide : true,
1870         /**
1871          * @event show
1872          * Fires after this menu is displayed
1873          * @param {Roo.menu.Menu} this
1874          */
1875         show : true,
1876         /**
1877          * @event hide
1878          * Fires after this menu is hidden
1879          * @param {Roo.menu.Menu} this
1880          */
1881         hide : true,
1882         /**
1883          * @event click
1884          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885          * @param {Roo.menu.Menu} this
1886          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887          * @param {Roo.EventObject} e
1888          */
1889         click : true,
1890         /**
1891          * @event mouseover
1892          * Fires when the mouse is hovering over this menu
1893          * @param {Roo.menu.Menu} this
1894          * @param {Roo.EventObject} e
1895          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1896          */
1897         mouseover : true,
1898         /**
1899          * @event mouseout
1900          * Fires when the mouse exits this menu
1901          * @param {Roo.menu.Menu} this
1902          * @param {Roo.EventObject} e
1903          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1904          */
1905         mouseout : true,
1906         /**
1907          * @event itemclick
1908          * Fires when a menu item contained in this menu is clicked
1909          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910          * @param {Roo.EventObject} e
1911          */
1912         itemclick: true
1913     });
1914     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1915 };
1916
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1918     
1919    /// html : false,
1920     //align : '',
1921     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1922     type: false,
1923     /**
1924      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1925      */
1926     registerMenu : true,
1927     
1928     menuItems :false, // stores the menu items..
1929     
1930     hidden:true,
1931     
1932     parentMenu : false,
1933     
1934     getChildContainer : function() {
1935         return this.el;  
1936     },
1937     
1938     getAutoCreate : function(){
1939          
1940         //if (['right'].indexOf(this.align)!==-1) {
1941         //    cfg.cn[1].cls += ' pull-right'
1942         //}
1943         
1944         
1945         var cfg = {
1946             tag : 'ul',
1947             cls : 'dropdown-menu' ,
1948             style : 'z-index:1000'
1949             
1950         }
1951         
1952         if (this.type === 'submenu') {
1953             cfg.cls = 'submenu active';
1954         }
1955         if (this.type === 'treeview') {
1956             cfg.cls = 'treeview-menu';
1957         }
1958         
1959         return cfg;
1960     },
1961     initEvents : function() {
1962         
1963        // Roo.log("ADD event");
1964        // Roo.log(this.triggerEl.dom);
1965         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1966         
1967         this.triggerEl.addClass('dropdown-toggle');
1968         this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1969
1970         this.el.on("mouseover", this.onMouseOver, this);
1971         this.el.on("mouseout", this.onMouseOut, this);
1972         
1973         
1974     },
1975     findTargetItem : function(e){
1976         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1977         if(!t){
1978             return false;
1979         }
1980         //Roo.log(t);         Roo.log(t.id);
1981         if(t && t.id){
1982             //Roo.log(this.menuitems);
1983             return this.menuitems.get(t.id);
1984             
1985             //return this.items.get(t.menuItemId);
1986         }
1987         
1988         return false;
1989     },
1990     onClick : function(e){
1991         Roo.log("menu.onClick");
1992         var t = this.findTargetItem(e);
1993         if(!t || t.isContainer){
1994             return;
1995         }
1996         Roo.log(e);
1997         /*
1998         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1999             if(t == this.activeItem && t.shouldDeactivate(e)){
2000                 this.activeItem.deactivate();
2001                 delete this.activeItem;
2002                 return;
2003             }
2004             if(t.canActivate){
2005                 this.setActiveItem(t, true);
2006             }
2007             return;
2008             
2009             
2010         }
2011         */
2012        
2013         Roo.log('pass click event');
2014         
2015         t.onClick(e);
2016         
2017         this.fireEvent("click", this, t, e);
2018         
2019         this.hide();
2020     },
2021      onMouseOver : function(e){
2022         var t  = this.findTargetItem(e);
2023         //Roo.log(t);
2024         //if(t){
2025         //    if(t.canActivate && !t.disabled){
2026         //        this.setActiveItem(t, true);
2027         //    }
2028         //}
2029         
2030         this.fireEvent("mouseover", this, e, t);
2031     },
2032     isVisible : function(){
2033         return !this.hidden;
2034     },
2035      onMouseOut : function(e){
2036         var t  = this.findTargetItem(e);
2037         
2038         //if(t ){
2039         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2040         //        this.activeItem.deactivate();
2041         //        delete this.activeItem;
2042         //    }
2043         //}
2044         this.fireEvent("mouseout", this, e, t);
2045     },
2046     
2047     
2048     /**
2049      * Displays this menu relative to another element
2050      * @param {String/HTMLElement/Roo.Element} element The element to align to
2051      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2052      * the element (defaults to this.defaultAlign)
2053      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2054      */
2055     show : function(el, pos, parentMenu){
2056         this.parentMenu = parentMenu;
2057         if(!this.el){
2058             this.render();
2059         }
2060         this.fireEvent("beforeshow", this);
2061         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2062     },
2063      /**
2064      * Displays this menu at a specific xy position
2065      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2066      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2067      */
2068     showAt : function(xy, parentMenu, /* private: */_e){
2069         this.parentMenu = parentMenu;
2070         if(!this.el){
2071             this.render();
2072         }
2073         if(_e !== false){
2074             this.fireEvent("beforeshow", this);
2075             //xy = this.el.adjustForConstraints(xy);
2076         }
2077         
2078         //this.el.show();
2079         this.hideMenuItems();
2080         this.hidden = false;
2081         this.triggerEl.addClass('open');
2082         
2083         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2084             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2085         }
2086         
2087         this.el.setXY(xy);
2088         this.focus();
2089         this.fireEvent("show", this);
2090     },
2091     
2092     focus : function(){
2093         return;
2094         if(!this.hidden){
2095             this.doFocus.defer(50, this);
2096         }
2097     },
2098
2099     doFocus : function(){
2100         if(!this.hidden){
2101             this.focusEl.focus();
2102         }
2103     },
2104
2105     /**
2106      * Hides this menu and optionally all parent menus
2107      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2108      */
2109     hide : function(deep){
2110         
2111         this.hideMenuItems();
2112         if(this.el && this.isVisible()){
2113             this.fireEvent("beforehide", this);
2114             if(this.activeItem){
2115                 this.activeItem.deactivate();
2116                 this.activeItem = null;
2117             }
2118             this.triggerEl.removeClass('open');;
2119             this.hidden = true;
2120             this.fireEvent("hide", this);
2121         }
2122         if(deep === true && this.parentMenu){
2123             this.parentMenu.hide(true);
2124         }
2125     },
2126     
2127     onTriggerPress  : function(e)
2128     {
2129         
2130         Roo.log('trigger press');
2131         //Roo.log(e.getTarget());
2132        // Roo.log(this.triggerEl.dom);
2133         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2134             return;
2135         }
2136         
2137         if (this.isVisible()) {
2138             Roo.log('hide');
2139             this.hide();
2140         } else {
2141             Roo.log('show');
2142             this.show(this.triggerEl, false, false);
2143         }
2144         
2145         e.stopEvent();
2146     },
2147     
2148          
2149        
2150     
2151     hideMenuItems : function()
2152     {
2153         //$(backdrop).remove()
2154         Roo.select('.open',true).each(function(aa) {
2155             
2156             aa.removeClass('open');
2157           //var parent = getParent($(this))
2158           //var relatedTarget = { relatedTarget: this }
2159           
2160            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2161           //if (e.isDefaultPrevented()) return
2162            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2163         })
2164     },
2165     addxtypeChild : function (tree, cntr) {
2166         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2167           
2168         this.menuitems.add(comp);
2169         return comp;
2170
2171     },
2172     getEl : function()
2173     {
2174         Roo.log(this.el);
2175         return this.el;
2176     }
2177 });
2178
2179  
2180  /*
2181  * - LGPL
2182  *
2183  * menu item
2184  * 
2185  */
2186
2187
2188 /**
2189  * @class Roo.bootstrap.MenuItem
2190  * @extends Roo.bootstrap.Component
2191  * Bootstrap MenuItem class
2192  * @cfg {String} html the menu label
2193  * @cfg {String} href the link
2194  * @cfg {Boolean} preventDefault (true | false) default true
2195  * @cfg {Boolean} isContainer (true | false) default false
2196  * 
2197  * 
2198  * @constructor
2199  * Create a new MenuItem
2200  * @param {Object} config The config object
2201  */
2202
2203
2204 Roo.bootstrap.MenuItem = function(config){
2205     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2206     this.addEvents({
2207         // raw events
2208         /**
2209          * @event click
2210          * The raw click event for the entire grid.
2211          * @param {Roo.bootstrap.MenuItem} this
2212          * @param {Roo.EventObject} e
2213          */
2214         "click" : true
2215     });
2216 };
2217
2218 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2219     
2220     href : false,
2221     html : false,
2222     preventDefault: true,
2223     isContainer : false,
2224     
2225     getAutoCreate : function(){
2226         
2227         if(this.isContainer){
2228             return {
2229                 tag: 'li',
2230                 cls: 'dropdown-menu-item'
2231             };
2232         }
2233         
2234         var cfg= {
2235             tag: 'li',
2236             cls: 'dropdown-menu-item',
2237             cn: [
2238                     {
2239                         tag : 'a',
2240                         href : '#',
2241                         html : 'Link'
2242                     }
2243                 ]
2244         };
2245         if (this.parent().type == 'treeview') {
2246             cfg.cls = 'treeview-menu';
2247         }
2248         
2249         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2250         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2251         return cfg;
2252     },
2253     
2254     initEvents: function() {
2255         
2256         //this.el.select('a').on('click', this.onClick, this);
2257         
2258     },
2259     onClick : function(e)
2260     {
2261         Roo.log('item on click ');
2262         //if(this.preventDefault){
2263         //    e.preventDefault();
2264         //}
2265         //this.parent().hideMenuItems();
2266         
2267         this.fireEvent('click', this, e);
2268     },
2269     getEl : function()
2270     {
2271         return this.el;
2272     }
2273 });
2274
2275  
2276
2277  /*
2278  * - LGPL
2279  *
2280  * menu separator
2281  * 
2282  */
2283
2284
2285 /**
2286  * @class Roo.bootstrap.MenuSeparator
2287  * @extends Roo.bootstrap.Component
2288  * Bootstrap MenuSeparator class
2289  * 
2290  * @constructor
2291  * Create a new MenuItem
2292  * @param {Object} config The config object
2293  */
2294
2295
2296 Roo.bootstrap.MenuSeparator = function(config){
2297     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2298 };
2299
2300 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2301     
2302     getAutoCreate : function(){
2303         var cfg = {
2304             cls: 'divider',
2305             tag : 'li'
2306         };
2307         
2308         return cfg;
2309     }
2310    
2311 });
2312
2313  
2314
2315  
2316 /*
2317 * Licence: LGPL
2318 */
2319
2320 /**
2321  * @class Roo.bootstrap.Modal
2322  * @extends Roo.bootstrap.Component
2323  * Bootstrap Modal class
2324  * @cfg {String} title Title of dialog
2325  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2326  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2327  * @cfg {Boolean} specificTitle default false
2328  * @cfg {Array} buttons Array of buttons or standard button set..
2329  * @cfg {String} buttonPosition (left|right|center) default right
2330  * @cfg {Boolean} animate default true
2331  * @cfg {Boolean} allow_close default true
2332  * 
2333  * @constructor
2334  * Create a new Modal Dialog
2335  * @param {Object} config The config object
2336  */
2337
2338 Roo.bootstrap.Modal = function(config){
2339     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2340     this.addEvents({
2341         // raw events
2342         /**
2343          * @event btnclick
2344          * The raw btnclick event for the button
2345          * @param {Roo.EventObject} e
2346          */
2347         "btnclick" : true
2348     });
2349     this.buttons = this.buttons || [];
2350      
2351     if (this.tmpl) {
2352         this.tmpl = Roo.factory(this.tmpl);
2353     }
2354     
2355 };
2356
2357 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2358     
2359     title : 'test dialog',
2360    
2361     buttons : false,
2362     
2363     // set on load...
2364      
2365     html: false,
2366     
2367     tmp: false,
2368     
2369     specificTitle: false,
2370     
2371     buttonPosition: 'right',
2372     
2373     allow_close : true,
2374     
2375     animate : true,
2376     
2377     
2378      // private
2379     bodyEl:  false,
2380     footerEl:  false,
2381     titleEl:  false,
2382     closeEl:  false,
2383     
2384     
2385     onRender : function(ct, position)
2386     {
2387         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2388      
2389         if(!this.el){
2390             var cfg = Roo.apply({},  this.getAutoCreate());
2391             cfg.id = Roo.id();
2392             //if(!cfg.name){
2393             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2394             //}
2395             //if (!cfg.name.length) {
2396             //    delete cfg.name;
2397            // }
2398             if (this.cls) {
2399                 cfg.cls += ' ' + this.cls;
2400             }
2401             if (this.style) {
2402                 cfg.style = this.style;
2403             }
2404             this.el = Roo.get(document.body).createChild(cfg, position);
2405         }
2406         //var type = this.el.dom.type;
2407         
2408         
2409         
2410         
2411         if(this.tabIndex !== undefined){
2412             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2413         }
2414         
2415         
2416         this.bodyEl = this.el.select('.modal-body',true).first();
2417         this.closeEl = this.el.select('.modal-header .close', true).first();
2418         this.footerEl = this.el.select('.modal-footer',true).first();
2419         this.titleEl = this.el.select('.modal-title',true).first();
2420         
2421         
2422          
2423         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2424         this.maskEl.enableDisplayMode("block");
2425         this.maskEl.hide();
2426         //this.el.addClass("x-dlg-modal");
2427     
2428         if (this.buttons.length) {
2429             Roo.each(this.buttons, function(bb) {
2430                 var b = Roo.apply({}, bb);
2431                 b.xns = b.xns || Roo.bootstrap;
2432                 b.xtype = b.xtype || 'Button';
2433                 if (typeof(b.listeners) == 'undefined') {
2434                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2435                 }
2436                 
2437                 var btn = Roo.factory(b);
2438                 
2439                 btn.onRender(this.el.select('.modal-footer div').first());
2440                 
2441             },this);
2442         }
2443         // render the children.
2444         var nitems = [];
2445         
2446         if(typeof(this.items) != 'undefined'){
2447             var items = this.items;
2448             delete this.items;
2449
2450             for(var i =0;i < items.length;i++) {
2451                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2452             }
2453         }
2454         
2455         this.items = nitems;
2456         
2457         // where are these used - they used to be body/close/footer
2458         
2459        
2460         this.initEvents();
2461         //this.el.addClass([this.fieldClass, this.cls]);
2462         
2463     },
2464     
2465     getAutoCreate : function(){
2466         
2467         
2468         var bdy = {
2469                 cls : 'modal-body',
2470                 html : this.html || ''
2471         };
2472         
2473         var title = {
2474             tag: 'h4',
2475             cls : 'modal-title',
2476             html : this.title
2477         };
2478         
2479         if(this.specificTitle){
2480             title = this.title;
2481             
2482         };
2483         
2484         var header = [];
2485         if (this.allow_close) {
2486             header.push({
2487                 tag: 'button',
2488                 cls : 'close',
2489                 html : '&times'
2490             });
2491         }
2492         header.push(title);
2493         
2494         var modal = {
2495             cls: "modal",
2496             style : 'display: none',
2497             cn : [
2498                 {
2499                     cls: "modal-dialog",
2500                     cn : [
2501                         {
2502                             cls : "modal-content",
2503                             cn : [
2504                                 {
2505                                     cls : 'modal-header',
2506                                     cn : header
2507                                 },
2508                                 bdy,
2509                                 {
2510                                     cls : 'modal-footer',
2511                                     cn : [
2512                                         {
2513                                             tag: 'div',
2514                                             cls: 'btn-' + this.buttonPosition
2515                                         }
2516                                     ]
2517                                     
2518                                 }
2519                                 
2520                                 
2521                             ]
2522                             
2523                         }
2524                     ]
2525                         
2526                 }
2527             ]
2528         };
2529         
2530         if(this.animate){
2531             modal.cls += ' fade';
2532         }
2533         
2534         return modal;
2535           
2536     },
2537     getChildContainer : function() {
2538          
2539          return this.bodyEl;
2540         
2541     },
2542     getButtonContainer : function() {
2543          return this.el.select('.modal-footer div',true).first();
2544         
2545     },
2546     initEvents : function()
2547     {
2548         if (this.allow_close) {
2549             this.closeEl.on('click', this.hide, this);
2550         }
2551         
2552         var _this = this;
2553         
2554         window.addEventListener("resize", function() { _this.resize(); } );
2555
2556     },
2557     
2558     resize : function()
2559     {
2560         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2561     },
2562     
2563     show : function() {
2564         
2565         if (!this.rendered) {
2566             this.render();
2567         }
2568         
2569         this.el.setStyle('display', 'block');
2570         
2571         if(this.animate){
2572             var _this = this;
2573             (function(){ _this.el.addClass('in'); }).defer(50);
2574         }else{
2575             this.el.addClass('in');
2576         }
2577         
2578         // not sure how we can show data in here.. 
2579         //if (this.tmpl) {
2580         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2581         //}
2582         
2583         Roo.get(document.body).addClass("x-body-masked");
2584         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2585         this.maskEl.show();
2586         this.el.setStyle('zIndex', '10001');
2587        
2588         this.fireEvent('show', this);
2589         
2590         
2591     },
2592     hide : function()
2593     {
2594         this.maskEl.hide();
2595         Roo.get(document.body).removeClass("x-body-masked");
2596         this.el.removeClass('in');
2597         
2598         if(this.animate){
2599             var _this = this;
2600             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2601         }else{
2602             this.el.setStyle('display', 'none');
2603         }
2604         
2605         this.fireEvent('hide', this);
2606     },
2607     
2608     addButton : function(str, cb)
2609     {
2610          
2611         
2612         var b = Roo.apply({}, { html : str } );
2613         b.xns = b.xns || Roo.bootstrap;
2614         b.xtype = b.xtype || 'Button';
2615         if (typeof(b.listeners) == 'undefined') {
2616             b.listeners = { click : cb.createDelegate(this)  };
2617         }
2618         
2619         var btn = Roo.factory(b);
2620            
2621         btn.onRender(this.el.select('.modal-footer div').first());
2622         
2623         return btn;   
2624        
2625     },
2626     
2627     setDefaultButton : function(btn)
2628     {
2629         //this.el.select('.modal-footer').()
2630     },
2631     resizeTo: function(w,h)
2632     {
2633         // skip..
2634     },
2635     setContentSize  : function(w, h)
2636     {
2637         
2638     },
2639     onButtonClick: function(btn,e)
2640     {
2641         //Roo.log([a,b,c]);
2642         this.fireEvent('btnclick', btn.name, e);
2643     },
2644      /**
2645      * Set the title of the Dialog
2646      * @param {String} str new Title
2647      */
2648     setTitle: function(str) {
2649         this.titleEl.dom.innerHTML = str;    
2650     },
2651     /**
2652      * Set the body of the Dialog
2653      * @param {String} str new Title
2654      */
2655     setBody: function(str) {
2656         this.bodyEl.dom.innerHTML = str;    
2657     },
2658     /**
2659      * Set the body of the Dialog using the template
2660      * @param {Obj} data - apply this data to the template and replace the body contents.
2661      */
2662     applyBody: function(obj)
2663     {
2664         if (!this.tmpl) {
2665             Roo.log("Error - using apply Body without a template");
2666             //code
2667         }
2668         this.tmpl.overwrite(this.bodyEl, obj);
2669     }
2670     
2671 });
2672
2673
2674 Roo.apply(Roo.bootstrap.Modal,  {
2675     /**
2676          * Button config that displays a single OK button
2677          * @type Object
2678          */
2679         OK :  [{
2680             name : 'ok',
2681             weight : 'primary',
2682             html : 'OK'
2683         }], 
2684         /**
2685          * Button config that displays Yes and No buttons
2686          * @type Object
2687          */
2688         YESNO : [
2689             {
2690                 name  : 'no',
2691                 html : 'No'
2692             },
2693             {
2694                 name  :'yes',
2695                 weight : 'primary',
2696                 html : 'Yes'
2697             }
2698         ],
2699         
2700         /**
2701          * Button config that displays OK and Cancel buttons
2702          * @type Object
2703          */
2704         OKCANCEL : [
2705             {
2706                name : 'cancel',
2707                 html : 'Cancel'
2708             },
2709             {
2710                 name : 'ok',
2711                 weight : 'primary',
2712                 html : 'OK'
2713             }
2714         ],
2715         /**
2716          * Button config that displays Yes, No and Cancel buttons
2717          * @type Object
2718          */
2719         YESNOCANCEL : [
2720             {
2721                 name : 'yes',
2722                 weight : 'primary',
2723                 html : 'Yes'
2724             },
2725             {
2726                 name : 'no',
2727                 html : 'No'
2728             },
2729             {
2730                 name : 'cancel',
2731                 html : 'Cancel'
2732             }
2733         ]
2734 });
2735  
2736  /*
2737  * - LGPL
2738  *
2739  * messagebox - can be used as a replace
2740  * 
2741  */
2742 /**
2743  * @class Roo.MessageBox
2744  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2745  * Example usage:
2746  *<pre><code>
2747 // Basic alert:
2748 Roo.Msg.alert('Status', 'Changes saved successfully.');
2749
2750 // Prompt for user data:
2751 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2752     if (btn == 'ok'){
2753         // process text value...
2754     }
2755 });
2756
2757 // Show a dialog using config options:
2758 Roo.Msg.show({
2759    title:'Save Changes?',
2760    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2761    buttons: Roo.Msg.YESNOCANCEL,
2762    fn: processResult,
2763    animEl: 'elId'
2764 });
2765 </code></pre>
2766  * @singleton
2767  */
2768 Roo.bootstrap.MessageBox = function(){
2769     var dlg, opt, mask, waitTimer;
2770     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2771     var buttons, activeTextEl, bwidth;
2772
2773     
2774     // private
2775     var handleButton = function(button){
2776         dlg.hide();
2777         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2778     };
2779
2780     // private
2781     var handleHide = function(){
2782         if(opt && opt.cls){
2783             dlg.el.removeClass(opt.cls);
2784         }
2785         //if(waitTimer){
2786         //    Roo.TaskMgr.stop(waitTimer);
2787         //    waitTimer = null;
2788         //}
2789     };
2790
2791     // private
2792     var updateButtons = function(b){
2793         var width = 0;
2794         if(!b){
2795             buttons["ok"].hide();
2796             buttons["cancel"].hide();
2797             buttons["yes"].hide();
2798             buttons["no"].hide();
2799             //dlg.footer.dom.style.display = 'none';
2800             return width;
2801         }
2802         dlg.footerEl.dom.style.display = '';
2803         for(var k in buttons){
2804             if(typeof buttons[k] != "function"){
2805                 if(b[k]){
2806                     buttons[k].show();
2807                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2808                     width += buttons[k].el.getWidth()+15;
2809                 }else{
2810                     buttons[k].hide();
2811                 }
2812             }
2813         }
2814         return width;
2815     };
2816
2817     // private
2818     var handleEsc = function(d, k, e){
2819         if(opt && opt.closable !== false){
2820             dlg.hide();
2821         }
2822         if(e){
2823             e.stopEvent();
2824         }
2825     };
2826
2827     return {
2828         /**
2829          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2830          * @return {Roo.BasicDialog} The BasicDialog element
2831          */
2832         getDialog : function(){
2833            if(!dlg){
2834                 dlg = new Roo.bootstrap.Modal( {
2835                     //draggable: true,
2836                     //resizable:false,
2837                     //constraintoviewport:false,
2838                     //fixedcenter:true,
2839                     //collapsible : false,
2840                     //shim:true,
2841                     //modal: true,
2842                   //  width:400,
2843                   //  height:100,
2844                     //buttonAlign:"center",
2845                     closeClick : function(){
2846                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2847                             handleButton("no");
2848                         }else{
2849                             handleButton("cancel");
2850                         }
2851                     }
2852                 });
2853                 dlg.render();
2854                 dlg.on("hide", handleHide);
2855                 mask = dlg.mask;
2856                 //dlg.addKeyListener(27, handleEsc);
2857                 buttons = {};
2858                 this.buttons = buttons;
2859                 var bt = this.buttonText;
2860                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2861                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2862                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2863                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2864                 Roo.log(buttons)
2865                 bodyEl = dlg.bodyEl.createChild({
2866
2867                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2868                         '<textarea class="roo-mb-textarea"></textarea>' +
2869                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2870                 });
2871                 msgEl = bodyEl.dom.firstChild;
2872                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2873                 textboxEl.enableDisplayMode();
2874                 textboxEl.addKeyListener([10,13], function(){
2875                     if(dlg.isVisible() && opt && opt.buttons){
2876                         if(opt.buttons.ok){
2877                             handleButton("ok");
2878                         }else if(opt.buttons.yes){
2879                             handleButton("yes");
2880                         }
2881                     }
2882                 });
2883                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2884                 textareaEl.enableDisplayMode();
2885                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2886                 progressEl.enableDisplayMode();
2887                 var pf = progressEl.dom.firstChild;
2888                 if (pf) {
2889                     pp = Roo.get(pf.firstChild);
2890                     pp.setHeight(pf.offsetHeight);
2891                 }
2892                 
2893             }
2894             return dlg;
2895         },
2896
2897         /**
2898          * Updates the message box body text
2899          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2900          * the XHTML-compliant non-breaking space character '&amp;#160;')
2901          * @return {Roo.MessageBox} This message box
2902          */
2903         updateText : function(text){
2904             if(!dlg.isVisible() && !opt.width){
2905                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2906             }
2907             msgEl.innerHTML = text || '&#160;';
2908       
2909             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2910             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2911             var w = Math.max(
2912                     Math.min(opt.width || cw , this.maxWidth), 
2913                     Math.max(opt.minWidth || this.minWidth, bwidth)
2914             );
2915             if(opt.prompt){
2916                 activeTextEl.setWidth(w);
2917             }
2918             if(dlg.isVisible()){
2919                 dlg.fixedcenter = false;
2920             }
2921             // to big, make it scroll. = But as usual stupid IE does not support
2922             // !important..
2923             
2924             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2925                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2926                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2927             } else {
2928                 bodyEl.dom.style.height = '';
2929                 bodyEl.dom.style.overflowY = '';
2930             }
2931             if (cw > w) {
2932                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2933             } else {
2934                 bodyEl.dom.style.overflowX = '';
2935             }
2936             
2937             dlg.setContentSize(w, bodyEl.getHeight());
2938             if(dlg.isVisible()){
2939                 dlg.fixedcenter = true;
2940             }
2941             return this;
2942         },
2943
2944         /**
2945          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2946          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2947          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2948          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2949          * @return {Roo.MessageBox} This message box
2950          */
2951         updateProgress : function(value, text){
2952             if(text){
2953                 this.updateText(text);
2954             }
2955             if (pp) { // weird bug on my firefox - for some reason this is not defined
2956                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2957             }
2958             return this;
2959         },        
2960
2961         /**
2962          * Returns true if the message box is currently displayed
2963          * @return {Boolean} True if the message box is visible, else false
2964          */
2965         isVisible : function(){
2966             return dlg && dlg.isVisible();  
2967         },
2968
2969         /**
2970          * Hides the message box if it is displayed
2971          */
2972         hide : function(){
2973             if(this.isVisible()){
2974                 dlg.hide();
2975             }  
2976         },
2977
2978         /**
2979          * Displays a new message box, or reinitializes an existing message box, based on the config options
2980          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2981          * The following config object properties are supported:
2982          * <pre>
2983 Property    Type             Description
2984 ----------  ---------------  ------------------------------------------------------------------------------------
2985 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2986                                    closes (defaults to undefined)
2987 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2988                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2989 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2990                                    progress and wait dialogs will ignore this property and always hide the
2991                                    close button as they can only be closed programmatically.
2992 cls               String           A custom CSS class to apply to the message box element
2993 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2994                                    displayed (defaults to 75)
2995 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2996                                    function will be btn (the name of the button that was clicked, if applicable,
2997                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2998                                    Progress and wait dialogs will ignore this option since they do not respond to
2999                                    user actions and can only be closed programmatically, so any required function
3000                                    should be called by the same code after it closes the dialog.
3001 icon              String           A CSS class that provides a background image to be used as an icon for
3002                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3003 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3004 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3005 modal             Boolean          False to allow user interaction with the page while the message box is
3006                                    displayed (defaults to true)
3007 msg               String           A string that will replace the existing message box body text (defaults
3008                                    to the XHTML-compliant non-breaking space character '&#160;')
3009 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3010 progress          Boolean          True to display a progress bar (defaults to false)
3011 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3012 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3013 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3014 title             String           The title text
3015 value             String           The string value to set into the active textbox element if displayed
3016 wait              Boolean          True to display a progress bar (defaults to false)
3017 width             Number           The width of the dialog in pixels
3018 </pre>
3019          *
3020          * Example usage:
3021          * <pre><code>
3022 Roo.Msg.show({
3023    title: 'Address',
3024    msg: 'Please enter your address:',
3025    width: 300,
3026    buttons: Roo.MessageBox.OKCANCEL,
3027    multiline: true,
3028    fn: saveAddress,
3029    animEl: 'addAddressBtn'
3030 });
3031 </code></pre>
3032          * @param {Object} config Configuration options
3033          * @return {Roo.MessageBox} This message box
3034          */
3035         show : function(options)
3036         {
3037             
3038             // this causes nightmares if you show one dialog after another
3039             // especially on callbacks..
3040              
3041             if(this.isVisible()){
3042                 
3043                 this.hide();
3044                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3045                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3046                 Roo.log("New Dialog Message:" +  options.msg )
3047                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3048                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3049                 
3050             }
3051             var d = this.getDialog();
3052             opt = options;
3053             d.setTitle(opt.title || "&#160;");
3054             d.closeEl.setDisplayed(opt.closable !== false);
3055             activeTextEl = textboxEl;
3056             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3057             if(opt.prompt){
3058                 if(opt.multiline){
3059                     textboxEl.hide();
3060                     textareaEl.show();
3061                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3062                         opt.multiline : this.defaultTextHeight);
3063                     activeTextEl = textareaEl;
3064                 }else{
3065                     textboxEl.show();
3066                     textareaEl.hide();
3067                 }
3068             }else{
3069                 textboxEl.hide();
3070                 textareaEl.hide();
3071             }
3072             progressEl.setDisplayed(opt.progress === true);
3073             this.updateProgress(0);
3074             activeTextEl.dom.value = opt.value || "";
3075             if(opt.prompt){
3076                 dlg.setDefaultButton(activeTextEl);
3077             }else{
3078                 var bs = opt.buttons;
3079                 var db = null;
3080                 if(bs && bs.ok){
3081                     db = buttons["ok"];
3082                 }else if(bs && bs.yes){
3083                     db = buttons["yes"];
3084                 }
3085                 dlg.setDefaultButton(db);
3086             }
3087             bwidth = updateButtons(opt.buttons);
3088             this.updateText(opt.msg);
3089             if(opt.cls){
3090                 d.el.addClass(opt.cls);
3091             }
3092             d.proxyDrag = opt.proxyDrag === true;
3093             d.modal = opt.modal !== false;
3094             d.mask = opt.modal !== false ? mask : false;
3095             if(!d.isVisible()){
3096                 // force it to the end of the z-index stack so it gets a cursor in FF
3097                 document.body.appendChild(dlg.el.dom);
3098                 d.animateTarget = null;
3099                 d.show(options.animEl);
3100             }
3101             return this;
3102         },
3103
3104         /**
3105          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3106          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3107          * and closing the message box when the process is complete.
3108          * @param {String} title The title bar text
3109          * @param {String} msg The message box body text
3110          * @return {Roo.MessageBox} This message box
3111          */
3112         progress : function(title, msg){
3113             this.show({
3114                 title : title,
3115                 msg : msg,
3116                 buttons: false,
3117                 progress:true,
3118                 closable:false,
3119                 minWidth: this.minProgressWidth,
3120                 modal : true
3121             });
3122             return this;
3123         },
3124
3125         /**
3126          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3127          * If a callback function is passed it will be called after the user clicks the button, and the
3128          * id of the button that was clicked will be passed as the only parameter to the callback
3129          * (could also be the top-right close button).
3130          * @param {String} title The title bar text
3131          * @param {String} msg The message box body text
3132          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3133          * @param {Object} scope (optional) The scope of the callback function
3134          * @return {Roo.MessageBox} This message box
3135          */
3136         alert : function(title, msg, fn, scope){
3137             this.show({
3138                 title : title,
3139                 msg : msg,
3140                 buttons: this.OK,
3141                 fn: fn,
3142                 scope : scope,
3143                 modal : true
3144             });
3145             return this;
3146         },
3147
3148         /**
3149          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3150          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3151          * You are responsible for closing the message box when the process is complete.
3152          * @param {String} msg The message box body text
3153          * @param {String} title (optional) The title bar text
3154          * @return {Roo.MessageBox} This message box
3155          */
3156         wait : function(msg, title){
3157             this.show({
3158                 title : title,
3159                 msg : msg,
3160                 buttons: false,
3161                 closable:false,
3162                 progress:true,
3163                 modal:true,
3164                 width:300,
3165                 wait:true
3166             });
3167             waitTimer = Roo.TaskMgr.start({
3168                 run: function(i){
3169                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3170                 },
3171                 interval: 1000
3172             });
3173             return this;
3174         },
3175
3176         /**
3177          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3178          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3179          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3180          * @param {String} title The title bar text
3181          * @param {String} msg The message box body text
3182          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3183          * @param {Object} scope (optional) The scope of the callback function
3184          * @return {Roo.MessageBox} This message box
3185          */
3186         confirm : function(title, msg, fn, scope){
3187             this.show({
3188                 title : title,
3189                 msg : msg,
3190                 buttons: this.YESNO,
3191                 fn: fn,
3192                 scope : scope,
3193                 modal : true
3194             });
3195             return this;
3196         },
3197
3198         /**
3199          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3200          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3201          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3202          * (could also be the top-right close button) and the text that was entered will be passed as the two
3203          * parameters to the callback.
3204          * @param {String} title The title bar text
3205          * @param {String} msg The message box body text
3206          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3207          * @param {Object} scope (optional) The scope of the callback function
3208          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3209          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3210          * @return {Roo.MessageBox} This message box
3211          */
3212         prompt : function(title, msg, fn, scope, multiline){
3213             this.show({
3214                 title : title,
3215                 msg : msg,
3216                 buttons: this.OKCANCEL,
3217                 fn: fn,
3218                 minWidth:250,
3219                 scope : scope,
3220                 prompt:true,
3221                 multiline: multiline,
3222                 modal : true
3223             });
3224             return this;
3225         },
3226
3227         /**
3228          * Button config that displays a single OK button
3229          * @type Object
3230          */
3231         OK : {ok:true},
3232         /**
3233          * Button config that displays Yes and No buttons
3234          * @type Object
3235          */
3236         YESNO : {yes:true, no:true},
3237         /**
3238          * Button config that displays OK and Cancel buttons
3239          * @type Object
3240          */
3241         OKCANCEL : {ok:true, cancel:true},
3242         /**
3243          * Button config that displays Yes, No and Cancel buttons
3244          * @type Object
3245          */
3246         YESNOCANCEL : {yes:true, no:true, cancel:true},
3247
3248         /**
3249          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3250          * @type Number
3251          */
3252         defaultTextHeight : 75,
3253         /**
3254          * The maximum width in pixels of the message box (defaults to 600)
3255          * @type Number
3256          */
3257         maxWidth : 600,
3258         /**
3259          * The minimum width in pixels of the message box (defaults to 100)
3260          * @type Number
3261          */
3262         minWidth : 100,
3263         /**
3264          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3265          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3266          * @type Number
3267          */
3268         minProgressWidth : 250,
3269         /**
3270          * An object containing the default button text strings that can be overriden for localized language support.
3271          * Supported properties are: ok, cancel, yes and no.
3272          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3273          * @type Object
3274          */
3275         buttonText : {
3276             ok : "OK",
3277             cancel : "Cancel",
3278             yes : "Yes",
3279             no : "No"
3280         }
3281     };
3282 }();
3283
3284 /**
3285  * Shorthand for {@link Roo.MessageBox}
3286  */
3287 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3288 Roo.Msg = Roo.Msg || Roo.MessageBox;
3289 /*
3290  * - LGPL
3291  *
3292  * navbar
3293  * 
3294  */
3295
3296 /**
3297  * @class Roo.bootstrap.Navbar
3298  * @extends Roo.bootstrap.Component
3299  * Bootstrap Navbar class
3300
3301  * @constructor
3302  * Create a new Navbar
3303  * @param {Object} config The config object
3304  */
3305
3306
3307 Roo.bootstrap.Navbar = function(config){
3308     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3309     
3310 };
3311
3312 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3313     
3314     
3315    
3316     // private
3317     navItems : false,
3318     loadMask : false,
3319     
3320     
3321     getAutoCreate : function(){
3322         
3323         
3324         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3325         
3326     },
3327     
3328     initEvents :function ()
3329     {
3330         //Roo.log(this.el.select('.navbar-toggle',true));
3331         this.el.select('.navbar-toggle',true).on('click', function() {
3332            // Roo.log('click');
3333             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3334         }, this);
3335         
3336         var mark = {
3337             tag: "div",
3338             cls:"x-dlg-mask"
3339         }
3340         
3341         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3342         
3343         var size = this.el.getSize();
3344         this.maskEl.setSize(size.width, size.height);
3345         this.maskEl.enableDisplayMode("block");
3346         this.maskEl.hide();
3347         
3348         if(this.loadMask){
3349             this.maskEl.show();
3350         }
3351     },
3352     
3353     
3354     getChildContainer : function()
3355     {
3356         if (this.el.select('.collapse').getCount()) {
3357             return this.el.select('.collapse',true).first();
3358         }
3359         
3360         return this.el;
3361     },
3362     
3363     mask : function()
3364     {
3365         this.maskEl.show();
3366     },
3367     
3368     unmask : function()
3369     {
3370         this.maskEl.hide();
3371     } 
3372     
3373     
3374     
3375     
3376 });
3377
3378
3379
3380  
3381
3382  /*
3383  * - LGPL
3384  *
3385  * navbar
3386  * 
3387  */
3388
3389 /**
3390  * @class Roo.bootstrap.NavSimplebar
3391  * @extends Roo.bootstrap.Navbar
3392  * Bootstrap Sidebar class
3393  *
3394  * @cfg {Boolean} inverse is inverted color
3395  * 
3396  * @cfg {String} type (nav | pills | tabs)
3397  * @cfg {Boolean} arrangement stacked | justified
3398  * @cfg {String} align (left | right) alignment
3399  * 
3400  * @cfg {Boolean} main (true|false) main nav bar? default false
3401  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3402  * 
3403  * @cfg {String} tag (header|footer|nav|div) default is nav 
3404
3405  * 
3406  * 
3407  * 
3408  * @constructor
3409  * Create a new Sidebar
3410  * @param {Object} config The config object
3411  */
3412
3413
3414 Roo.bootstrap.NavSimplebar = function(config){
3415     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3416 };
3417
3418 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3419     
3420     inverse: false,
3421     
3422     type: false,
3423     arrangement: '',
3424     align : false,
3425     
3426     
3427     
3428     main : false,
3429     
3430     
3431     tag : false,
3432     
3433     
3434     getAutoCreate : function(){
3435         
3436         
3437         var cfg = {
3438             tag : this.tag || 'div',
3439             cls : 'navbar'
3440         };
3441           
3442         
3443         cfg.cn = [
3444             {
3445                 cls: 'nav',
3446                 tag : 'ul'
3447             }
3448         ];
3449         
3450          
3451         this.type = this.type || 'nav';
3452         if (['tabs','pills'].indexOf(this.type)!==-1) {
3453             cfg.cn[0].cls += ' nav-' + this.type
3454         
3455         
3456         } else {
3457             if (this.type!=='nav') {
3458                 Roo.log('nav type must be nav/tabs/pills')
3459             }
3460             cfg.cn[0].cls += ' navbar-nav'
3461         }
3462         
3463         
3464         
3465         
3466         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3467             cfg.cn[0].cls += ' nav-' + this.arrangement;
3468         }
3469         
3470         
3471         if (this.align === 'right') {
3472             cfg.cn[0].cls += ' navbar-right';
3473         }
3474         
3475         if (this.inverse) {
3476             cfg.cls += ' navbar-inverse';
3477             
3478         }
3479         
3480         
3481         return cfg;
3482     
3483         
3484     }
3485     
3486     
3487     
3488 });
3489
3490
3491
3492  
3493
3494  
3495        /*
3496  * - LGPL
3497  *
3498  * navbar
3499  * 
3500  */
3501
3502 /**
3503  * @class Roo.bootstrap.NavHeaderbar
3504  * @extends Roo.bootstrap.NavSimplebar
3505  * Bootstrap Sidebar class
3506  *
3507  * @cfg {String} brand what is brand
3508  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3509  * @cfg {String} brand_href href of the brand
3510  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3511  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3512  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3513  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3514  * 
3515  * @constructor
3516  * Create a new Sidebar
3517  * @param {Object} config The config object
3518  */
3519
3520
3521 Roo.bootstrap.NavHeaderbar = function(config){
3522     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3523       
3524 };
3525
3526 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3527     
3528     position: '',
3529     brand: '',
3530     brand_href: false,
3531     srButton : true,
3532     autohide : false,
3533     desktopCenter : false,
3534    
3535     
3536     getAutoCreate : function(){
3537         
3538         var   cfg = {
3539             tag: this.nav || 'nav',
3540             cls: 'navbar',
3541             role: 'navigation',
3542             cn: []
3543         };
3544         
3545         var cn = cfg.cn;
3546         if (this.desktopCenter) {
3547             cn.push({cls : 'container', cn : []});
3548             cn = cn[0].cn;
3549         }
3550         
3551         if(this.srButton){
3552             cn.push({
3553                 tag: 'div',
3554                 cls: 'navbar-header',
3555                 cn: [
3556                     {
3557                         tag: 'button',
3558                         type: 'button',
3559                         cls: 'navbar-toggle',
3560                         'data-toggle': 'collapse',
3561                         cn: [
3562                             {
3563                                 tag: 'span',
3564                                 cls: 'sr-only',
3565                                 html: 'Toggle navigation'
3566                             },
3567                             {
3568                                 tag: 'span',
3569                                 cls: 'icon-bar'
3570                             },
3571                             {
3572                                 tag: 'span',
3573                                 cls: 'icon-bar'
3574                             },
3575                             {
3576                                 tag: 'span',
3577                                 cls: 'icon-bar'
3578                             }
3579                         ]
3580                     }
3581                 ]
3582             });
3583         }
3584         
3585         cn.push({
3586             tag: 'div',
3587             cls: 'collapse navbar-collapse',
3588             cn : []
3589         });
3590         
3591         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3592         
3593         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3594             cfg.cls += ' navbar-' + this.position;
3595             
3596             // tag can override this..
3597             
3598             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3599         }
3600         
3601         if (this.brand !== '') {
3602             cn[0].cn.push({
3603                 tag: 'a',
3604                 href: this.brand_href ? this.brand_href : '#',
3605                 cls: 'navbar-brand',
3606                 cn: [
3607                 this.brand
3608                 ]
3609             });
3610         }
3611         
3612         if(this.main){
3613             cfg.cls += ' main-nav';
3614         }
3615         
3616         
3617         return cfg;
3618
3619         
3620     },
3621     getHeaderChildContainer : function()
3622     {
3623         if (this.el.select('.navbar-header').getCount()) {
3624             return this.el.select('.navbar-header',true).first();
3625         }
3626         
3627         return this.getChildContainer();
3628     },
3629     
3630     
3631     initEvents : function()
3632     {
3633         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3634         
3635         if (this.autohide) {
3636             
3637             var prevScroll = 0;
3638             var ft = this.el;
3639             
3640             Roo.get(document).on('scroll',function(e) {
3641                 var ns = Roo.get(document).getScroll().top;
3642                 var os = prevScroll;
3643                 prevScroll = ns;
3644                 
3645                 if(ns > os){
3646                     ft.removeClass('slideDown');
3647                     ft.addClass('slideUp');
3648                     return;
3649                 }
3650                 ft.removeClass('slideUp');
3651                 ft.addClass('slideDown');
3652                  
3653               
3654           },this);
3655         }
3656     }    
3657     
3658 });
3659
3660
3661
3662  
3663
3664  /*
3665  * - LGPL
3666  *
3667  * navbar
3668  * 
3669  */
3670
3671 /**
3672  * @class Roo.bootstrap.NavSidebar
3673  * @extends Roo.bootstrap.Navbar
3674  * Bootstrap Sidebar class
3675  * 
3676  * @constructor
3677  * Create a new Sidebar
3678  * @param {Object} config The config object
3679  */
3680
3681
3682 Roo.bootstrap.NavSidebar = function(config){
3683     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3684 };
3685
3686 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3687     
3688     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3689     
3690     getAutoCreate : function(){
3691         
3692         
3693         return  {
3694             tag: 'div',
3695             cls: 'sidebar sidebar-nav'
3696         };
3697     
3698         
3699     }
3700     
3701     
3702     
3703 });
3704
3705
3706
3707  
3708
3709  /*
3710  * - LGPL
3711  *
3712  * nav group
3713  * 
3714  */
3715
3716 /**
3717  * @class Roo.bootstrap.NavGroup
3718  * @extends Roo.bootstrap.Component
3719  * Bootstrap NavGroup class
3720  * @cfg {String} align (left|right)
3721  * @cfg {Boolean} inverse
3722  * @cfg {String} type (nav|pills|tab) default nav
3723  * @cfg {String} navId - reference Id for navbar.
3724
3725  * 
3726  * @constructor
3727  * Create a new nav group
3728  * @param {Object} config The config object
3729  */
3730
3731 Roo.bootstrap.NavGroup = function(config){
3732     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3733     this.navItems = [];
3734    
3735     Roo.bootstrap.NavGroup.register(this);
3736      this.addEvents({
3737         /**
3738              * @event changed
3739              * Fires when the active item changes
3740              * @param {Roo.bootstrap.NavGroup} this
3741              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3742              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3743          */
3744         'changed': true
3745      });
3746     
3747 };
3748
3749 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3750     
3751     align: '',
3752     inverse: false,
3753     form: false,
3754     type: 'nav',
3755     navId : '',
3756     // private
3757     
3758     navItems : false, 
3759     
3760     getAutoCreate : function()
3761     {
3762         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3763         
3764         cfg = {
3765             tag : 'ul',
3766             cls: 'nav' 
3767         }
3768         
3769         if (['tabs','pills'].indexOf(this.type)!==-1) {
3770             cfg.cls += ' nav-' + this.type
3771         } else {
3772             if (this.type!=='nav') {
3773                 Roo.log('nav type must be nav/tabs/pills')
3774             }
3775             cfg.cls += ' navbar-nav'
3776         }
3777         
3778         if (this.parent().sidebar) {
3779             cfg = {
3780                 tag: 'ul',
3781                 cls: 'dashboard-menu sidebar-menu'
3782             }
3783             
3784             return cfg;
3785         }
3786         
3787         if (this.form === true) {
3788             cfg = {
3789                 tag: 'form',
3790                 cls: 'navbar-form'
3791             }
3792             
3793             if (this.align === 'right') {
3794                 cfg.cls += ' navbar-right';
3795             } else {
3796                 cfg.cls += ' navbar-left';
3797             }
3798         }
3799         
3800         if (this.align === 'right') {
3801             cfg.cls += ' navbar-right';
3802         }
3803         
3804         if (this.inverse) {
3805             cfg.cls += ' navbar-inverse';
3806             
3807         }
3808         
3809         
3810         return cfg;
3811     },
3812     /**
3813     * sets the active Navigation item
3814     * @param {Roo.bootstrap.NavItem} the new current navitem
3815     */
3816     setActiveItem : function(item)
3817     {
3818         var prev = false;
3819         Roo.each(this.navItems, function(v){
3820             if (v == item) {
3821                 return ;
3822             }
3823             if (v.isActive()) {
3824                 v.setActive(false, true);
3825                 prev = v;
3826                 
3827             }
3828             
3829         });
3830
3831         item.setActive(true, true);
3832         this.fireEvent('changed', this, item, prev);
3833         
3834         
3835     },
3836     /**
3837     * gets the active Navigation item
3838     * @return {Roo.bootstrap.NavItem} the current navitem
3839     */
3840     getActive : function()
3841     {
3842         
3843         var prev = false;
3844         Roo.each(this.navItems, function(v){
3845             
3846             if (v.isActive()) {
3847                 prev = v;
3848                 
3849             }
3850             
3851         });
3852         return prev;
3853     },
3854     
3855     indexOfNav : function()
3856     {
3857         
3858         var prev = false;
3859         Roo.each(this.navItems, function(v,i){
3860             
3861             if (v.isActive()) {
3862                 prev = i;
3863                 
3864             }
3865             
3866         });
3867         return prev;
3868     },
3869     /**
3870     * adds a Navigation item
3871     * @param {Roo.bootstrap.NavItem} the navitem to add
3872     */
3873     addItem : function(cfg)
3874     {
3875         var cn = new Roo.bootstrap.NavItem(cfg);
3876         this.register(cn);
3877         cn.parentId = this.id;
3878         cn.onRender(this.el, null);
3879         return cn;
3880     },
3881     /**
3882     * register a Navigation item
3883     * @param {Roo.bootstrap.NavItem} the navitem to add
3884     */
3885     register : function(item)
3886     {
3887         this.navItems.push( item);
3888         item.navId = this.navId;
3889     
3890     },
3891     
3892     /**
3893     * clear all the Navigation item
3894     */
3895    
3896     clearAll : function()
3897     {
3898         this.navItems = [];
3899         this.el.dom.innerHTML = '';
3900     },
3901     
3902     getNavItem: function(tabId)
3903     {
3904         var ret = false;
3905         Roo.each(this.navItems, function(e) {
3906             if (e.tabId == tabId) {
3907                ret =  e;
3908                return false;
3909             }
3910             return true;
3911             
3912         });
3913         return ret;
3914     },
3915     
3916     setActiveNext : function()
3917     {
3918         var i = this.indexOfNav(this.getActive());
3919         if (i > this.navItems.length) {
3920             return;
3921         }
3922         this.setActiveItem(this.navItems[i+1]);
3923     },
3924     setActivePrev : function()
3925     {
3926         var i = this.indexOfNav(this.getActive());
3927         if (i  < 1) {
3928             return;
3929         }
3930         this.setActiveItem(this.navItems[i-1]);
3931     },
3932     clearWasActive : function(except) {
3933         Roo.each(this.navItems, function(e) {
3934             if (e.tabId != except.tabId && e.was_active) {
3935                e.was_active = false;
3936                return false;
3937             }
3938             return true;
3939             
3940         });
3941     },
3942     getWasActive : function ()
3943     {
3944         var r = false;
3945         Roo.each(this.navItems, function(e) {
3946             if (e.was_active) {
3947                r = e;
3948                return false;
3949             }
3950             return true;
3951             
3952         });
3953         return r;
3954     }
3955     
3956     
3957 });
3958
3959  
3960 Roo.apply(Roo.bootstrap.NavGroup, {
3961     
3962     groups: {},
3963      /**
3964     * register a Navigation Group
3965     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3966     */
3967     register : function(navgrp)
3968     {
3969         this.groups[navgrp.navId] = navgrp;
3970         
3971     },
3972     /**
3973     * fetch a Navigation Group based on the navigation ID
3974     * @param {string} the navgroup to add
3975     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3976     */
3977     get: function(navId) {
3978         if (typeof(this.groups[navId]) == 'undefined') {
3979             return false;
3980             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3981         }
3982         return this.groups[navId] ;
3983     }
3984     
3985     
3986     
3987 });
3988
3989  /*
3990  * - LGPL
3991  *
3992  * row
3993  * 
3994  */
3995
3996 /**
3997  * @class Roo.bootstrap.NavItem
3998  * @extends Roo.bootstrap.Component
3999  * Bootstrap Navbar.NavItem class
4000  * @cfg {String} href  link to
4001  * @cfg {String} html content of button
4002  * @cfg {String} badge text inside badge
4003  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4004  * @cfg {String} glyphicon name of glyphicon
4005  * @cfg {String} icon name of font awesome icon
4006  * @cfg {Boolean} active Is item active
4007  * @cfg {Boolean} disabled Is item disabled
4008  
4009  * @cfg {Boolean} preventDefault (true | false) default false
4010  * @cfg {String} tabId the tab that this item activates.
4011  * @cfg {String} tagtype (a|span) render as a href or span?
4012  * @cfg {Boolean} animateRef (true|false) link to element default false  
4013   
4014  * @constructor
4015  * Create a new Navbar Item
4016  * @param {Object} config The config object
4017  */
4018 Roo.bootstrap.NavItem = function(config){
4019     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4020     this.addEvents({
4021         // raw events
4022         /**
4023          * @event click
4024          * The raw click event for the entire grid.
4025          * @param {Roo.EventObject} e
4026          */
4027         "click" : true,
4028          /**
4029             * @event changed
4030             * Fires when the active item active state changes
4031             * @param {Roo.bootstrap.NavItem} this
4032             * @param {boolean} state the new state
4033              
4034          */
4035         'changed': true,
4036         /**
4037             * @event scrollto
4038             * Fires when scroll to element
4039             * @param {Roo.bootstrap.NavItem} this
4040             * @param {Object} options
4041             * @param {Roo.EventObject} e
4042              
4043          */
4044         'scrollto': true
4045     });
4046    
4047 };
4048
4049 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4050     
4051     href: false,
4052     html: '',
4053     badge: '',
4054     icon: false,
4055     glyphicon: false,
4056     active: false,
4057     preventDefault : false,
4058     tabId : false,
4059     tagtype : 'a',
4060     disabled : false,
4061     animateRef : false,
4062     was_active : false,
4063     
4064     getAutoCreate : function(){
4065          
4066         var cfg = {
4067             tag: 'li',
4068             cls: 'nav-item'
4069             
4070         }
4071         if (this.active) {
4072             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4073         }
4074         if (this.disabled) {
4075             cfg.cls += ' disabled';
4076         }
4077         
4078         if (this.href || this.html || this.glyphicon || this.icon) {
4079             cfg.cn = [
4080                 {
4081                     tag: this.tagtype,
4082                     href : this.href || "#",
4083                     html: this.html || ''
4084                 }
4085             ];
4086             
4087             if (this.icon) {
4088                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4089             }
4090
4091             if(this.glyphicon) {
4092                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4093             }
4094             
4095             if (this.menu) {
4096                 
4097                 cfg.cn[0].html += " <span class='caret'></span>";
4098              
4099             }
4100             
4101             if (this.badge !== '') {
4102                  
4103                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4104             }
4105         }
4106         
4107         
4108         
4109         return cfg;
4110     },
4111     initEvents: function() 
4112     {
4113         if (typeof (this.menu) != 'undefined') {
4114             this.menu.parentType = this.xtype;
4115             this.menu.triggerEl = this.el;
4116             this.menu = this.addxtype(Roo.apply({}, this.menu));
4117         }
4118         
4119         this.el.select('a',true).on('click', this.onClick, this);
4120         
4121         if(this.tagtype == 'span'){
4122             this.el.select('span',true).on('click', this.onClick, this);
4123         }
4124        
4125         // at this point parent should be available..
4126         this.parent().register(this);
4127     },
4128     
4129     onClick : function(e)
4130     {
4131         if(
4132                 this.preventDefault || 
4133                 this.href == '#' 
4134         ){
4135             
4136             e.preventDefault();
4137         }
4138         
4139         if (this.disabled) {
4140             return;
4141         }
4142         
4143         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4144         if (tg && tg.transition) {
4145             Roo.log("waiting for the transitionend");
4146             return;
4147         }
4148         
4149         
4150         
4151         //Roo.log("fire event clicked");
4152         if(this.fireEvent('click', this, e) === false){
4153             return;
4154         };
4155         
4156         if(this.tagtype == 'span'){
4157             return;
4158         }
4159         
4160         //Roo.log(this.href);
4161         var ael = this.el.select('a',true).first();
4162         //Roo.log(ael);
4163         
4164         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4165             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4166             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4167                 return; // ignore... - it's a 'hash' to another page.
4168             }
4169             
4170             e.preventDefault();
4171             this.scrollToElement(e);
4172         }
4173         
4174         
4175         var p =  this.parent();
4176    
4177         if (['tabs','pills'].indexOf(p.type)!==-1) {
4178             if (typeof(p.setActiveItem) !== 'undefined') {
4179                 p.setActiveItem(this);
4180             }
4181         }
4182         
4183         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4184         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4185             // remove the collapsed menu expand...
4186             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4187         }
4188     },
4189     
4190     isActive: function () {
4191         return this.active
4192     },
4193     setActive : function(state, fire, is_was_active)
4194     {
4195         if (this.active && !state && this.navId) {
4196             this.was_active = true;
4197             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4198             if (nv) {
4199                 nv.clearWasActive(this);
4200             }
4201             
4202         }
4203         this.active = state;
4204         
4205         if (!state ) {
4206             this.el.removeClass('active');
4207         } else if (!this.el.hasClass('active')) {
4208             this.el.addClass('active');
4209         }
4210         if (fire) {
4211             this.fireEvent('changed', this, state);
4212         }
4213         
4214         // show a panel if it's registered and related..
4215         
4216         if (!this.navId || !this.tabId || !state || is_was_active) {
4217             return;
4218         }
4219         
4220         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4221         if (!tg) {
4222             return;
4223         }
4224         var pan = tg.getPanelByName(this.tabId);
4225         if (!pan) {
4226             return;
4227         }
4228         // if we can not flip to new panel - go back to old nav highlight..
4229         if (false == tg.showPanel(pan)) {
4230             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4231             if (nv) {
4232                 var onav = nv.getWasActive();
4233                 if (onav) {
4234                     onav.setActive(true, false, true);
4235                 }
4236             }
4237             
4238         }
4239         
4240         
4241         
4242     },
4243      // this should not be here...
4244     setDisabled : function(state)
4245     {
4246         this.disabled = state;
4247         if (!state ) {
4248             this.el.removeClass('disabled');
4249         } else if (!this.el.hasClass('disabled')) {
4250             this.el.addClass('disabled');
4251         }
4252         
4253     },
4254     
4255     /**
4256      * Fetch the element to display the tooltip on.
4257      * @return {Roo.Element} defaults to this.el
4258      */
4259     tooltipEl : function()
4260     {
4261         return this.el.select('' + this.tagtype + '', true).first();
4262     },
4263     
4264     scrollToElement : function(e)
4265     {
4266         var c = document.body;
4267         
4268         /*
4269          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4270          */
4271         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4272             c = document.documentElement;
4273         }
4274         
4275         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4276         
4277         if(!target){
4278             return;
4279         }
4280
4281         var o = target.calcOffsetsTo(c);
4282         
4283         var options = {
4284             target : target,
4285             value : o[1]
4286         }
4287         
4288         this.fireEvent('scrollto', this, options, e);
4289         
4290         Roo.get(c).scrollTo('top', options.value, true);
4291         
4292         return;
4293     }
4294 });
4295  
4296
4297  /*
4298  * - LGPL
4299  *
4300  * sidebar item
4301  *
4302  *  li
4303  *    <span> icon </span>
4304  *    <span> text </span>
4305  *    <span>badge </span>
4306  */
4307
4308 /**
4309  * @class Roo.bootstrap.NavSidebarItem
4310  * @extends Roo.bootstrap.NavItem
4311  * Bootstrap Navbar.NavSidebarItem class
4312  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4313  * @constructor
4314  * Create a new Navbar Button
4315  * @param {Object} config The config object
4316  */
4317 Roo.bootstrap.NavSidebarItem = function(config){
4318     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4319     this.addEvents({
4320         // raw events
4321         /**
4322          * @event click
4323          * The raw click event for the entire grid.
4324          * @param {Roo.EventObject} e
4325          */
4326         "click" : true,
4327          /**
4328             * @event changed
4329             * Fires when the active item active state changes
4330             * @param {Roo.bootstrap.NavSidebarItem} this
4331             * @param {boolean} state the new state
4332              
4333          */
4334         'changed': true
4335     });
4336    
4337 };
4338
4339 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4340     
4341     badgeWeight : 'default',
4342     
4343     getAutoCreate : function(){
4344         
4345         
4346         var a = {
4347                 tag: 'a',
4348                 href : this.href || '#',
4349                 cls: '',
4350                 html : '',
4351                 cn : []
4352         };
4353         var cfg = {
4354             tag: 'li',
4355             cls: '',
4356             cn: [ a ]
4357         };
4358         var span = {
4359             tag: 'span',
4360             html : this.html || ''
4361         };
4362         
4363         
4364         if (this.active) {
4365             cfg.cls += ' active';
4366         }
4367         
4368         if (this.disabled) {
4369             cfg.cls += ' disabled';
4370         }
4371         
4372         // left icon..
4373         if (this.glyphicon || this.icon) {
4374             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4375             a.cn.push({ tag : 'i', cls : c }) ;
4376         }
4377         // html..
4378         a.cn.push(span);
4379         // then badge..
4380         if (this.badge !== '') {
4381             
4382             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4383         }
4384         // fi
4385         if (this.menu) {
4386             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4387             a.cls += 'dropdown-toggle treeview' ;
4388             
4389         }
4390         
4391         
4392         
4393         return cfg;
4394          
4395            
4396     },
4397     
4398     initEvents : function()
4399     { 
4400         this.el.on('click', this.onClick, this);
4401        
4402     
4403         if(this.badge !== ''){
4404  
4405             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4406         }
4407         
4408     },
4409     
4410     onClick : function(e)
4411     {
4412         if(this.disabled){
4413             e.preventDefault();
4414             return;
4415         }
4416         
4417         if(this.preventDefault){
4418             e.preventDefault();
4419         }
4420         
4421         this.fireEvent('click', this);
4422     },
4423     
4424     disable : function()
4425     {
4426         this.setDisabled(true);
4427     },
4428     
4429     enable : function()
4430     {
4431         this.setDisabled(false);
4432     },
4433     
4434     setDisabled : function(state)
4435     {
4436         if(this.disabled == state){
4437             return;
4438         }
4439         
4440         this.disabled = state;
4441         
4442         if (state) {
4443             this.el.addClass('disabled');
4444             return;
4445         }
4446         
4447         this.el.removeClass('disabled');
4448         
4449         return;
4450     },
4451     
4452     setActive : function(state)
4453     {
4454         if(this.active == state){
4455             return;
4456         }
4457         
4458         this.active = state;
4459         
4460         if (state) {
4461             this.el.addClass('active');
4462             return;
4463         }
4464         
4465         this.el.removeClass('active');
4466         
4467         return;
4468     },
4469     
4470     isActive: function () 
4471     {
4472         return this.active;
4473     },
4474     
4475     setBadge : function(str)
4476     {
4477         if(!this.badgeEl){
4478             return;
4479         }
4480         
4481         this.badgeEl.dom.innerHTML = str;
4482     }
4483     
4484    
4485      
4486  
4487 });
4488  
4489
4490  /*
4491  * - LGPL
4492  *
4493  * row
4494  * 
4495  */
4496
4497 /**
4498  * @class Roo.bootstrap.Row
4499  * @extends Roo.bootstrap.Component
4500  * Bootstrap Row class (contains columns...)
4501  * 
4502  * @constructor
4503  * Create a new Row
4504  * @param {Object} config The config object
4505  */
4506
4507 Roo.bootstrap.Row = function(config){
4508     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4509 };
4510
4511 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4512     
4513     getAutoCreate : function(){
4514        return {
4515             cls: 'row clearfix'
4516        };
4517     }
4518     
4519     
4520 });
4521
4522  
4523
4524  /*
4525  * - LGPL
4526  *
4527  * element
4528  * 
4529  */
4530
4531 /**
4532  * @class Roo.bootstrap.Element
4533  * @extends Roo.bootstrap.Component
4534  * Bootstrap Element class
4535  * @cfg {String} html contents of the element
4536  * @cfg {String} tag tag of the element
4537  * @cfg {String} cls class of the element
4538  * @cfg {Boolean} preventDefault (true|false) default false
4539  * @cfg {Boolean} clickable (true|false) default false
4540  * 
4541  * @constructor
4542  * Create a new Element
4543  * @param {Object} config The config object
4544  */
4545
4546 Roo.bootstrap.Element = function(config){
4547     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4548     
4549     this.addEvents({
4550         // raw events
4551         /**
4552          * @event click
4553          * When a element is chick
4554          * @param {Roo.bootstrap.Element} this
4555          * @param {Roo.EventObject} e
4556          */
4557         "click" : true
4558     });
4559 };
4560
4561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4562     
4563     tag: 'div',
4564     cls: '',
4565     html: '',
4566     preventDefault: false, 
4567     clickable: false,
4568     
4569     getAutoCreate : function(){
4570         
4571         var cfg = {
4572             tag: this.tag,
4573             cls: this.cls,
4574             html: this.html
4575         }
4576         
4577         return cfg;
4578     },
4579     
4580     initEvents: function() 
4581     {
4582         Roo.bootstrap.Element.superclass.initEvents.call(this);
4583         
4584         if(this.clickable){
4585             this.el.on('click', this.onClick, this);
4586         }
4587         
4588     },
4589     
4590     onClick : function(e)
4591     {
4592         if(this.preventDefault){
4593             e.preventDefault();
4594         }
4595         
4596         this.fireEvent('click', this, e);
4597     },
4598     
4599     getValue : function()
4600     {
4601         return this.el.dom.innerHTML;
4602     },
4603     
4604     setValue : function(value)
4605     {
4606         this.el.dom.innerHTML = value;
4607     }
4608    
4609 });
4610
4611  
4612
4613  /*
4614  * - LGPL
4615  *
4616  * pagination
4617  * 
4618  */
4619
4620 /**
4621  * @class Roo.bootstrap.Pagination
4622  * @extends Roo.bootstrap.Component
4623  * Bootstrap Pagination class
4624  * @cfg {String} size xs | sm | md | lg
4625  * @cfg {Boolean} inverse false | true
4626  * 
4627  * @constructor
4628  * Create a new Pagination
4629  * @param {Object} config The config object
4630  */
4631
4632 Roo.bootstrap.Pagination = function(config){
4633     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4634 };
4635
4636 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4637     
4638     cls: false,
4639     size: false,
4640     inverse: false,
4641     
4642     getAutoCreate : function(){
4643         var cfg = {
4644             tag: 'ul',
4645                 cls: 'pagination'
4646         };
4647         if (this.inverse) {
4648             cfg.cls += ' inverse';
4649         }
4650         if (this.html) {
4651             cfg.html=this.html;
4652         }
4653         if (this.cls) {
4654             cfg.cls += " " + this.cls;
4655         }
4656         return cfg;
4657     }
4658    
4659 });
4660
4661  
4662
4663  /*
4664  * - LGPL
4665  *
4666  * Pagination item
4667  * 
4668  */
4669
4670
4671 /**
4672  * @class Roo.bootstrap.PaginationItem
4673  * @extends Roo.bootstrap.Component
4674  * Bootstrap PaginationItem class
4675  * @cfg {String} html text
4676  * @cfg {String} href the link
4677  * @cfg {Boolean} preventDefault (true | false) default true
4678  * @cfg {Boolean} active (true | false) default false
4679  * @cfg {Boolean} disabled default false
4680  * 
4681  * 
4682  * @constructor
4683  * Create a new PaginationItem
4684  * @param {Object} config The config object
4685  */
4686
4687
4688 Roo.bootstrap.PaginationItem = function(config){
4689     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4690     this.addEvents({
4691         // raw events
4692         /**
4693          * @event click
4694          * The raw click event for the entire grid.
4695          * @param {Roo.EventObject} e
4696          */
4697         "click" : true
4698     });
4699 };
4700
4701 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4702     
4703     href : false,
4704     html : false,
4705     preventDefault: true,
4706     active : false,
4707     cls : false,
4708     disabled: false,
4709     
4710     getAutoCreate : function(){
4711         var cfg= {
4712             tag: 'li',
4713             cn: [
4714                 {
4715                     tag : 'a',
4716                     href : this.href ? this.href : '#',
4717                     html : this.html ? this.html : ''
4718                 }
4719             ]
4720         };
4721         
4722         if(this.cls){
4723             cfg.cls = this.cls;
4724         }
4725         
4726         if(this.disabled){
4727             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4728         }
4729         
4730         if(this.active){
4731             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4732         }
4733         
4734         return cfg;
4735     },
4736     
4737     initEvents: function() {
4738         
4739         this.el.on('click', this.onClick, this);
4740         
4741     },
4742     onClick : function(e)
4743     {
4744         Roo.log('PaginationItem on click ');
4745         if(this.preventDefault){
4746             e.preventDefault();
4747         }
4748         
4749         if(this.disabled){
4750             return;
4751         }
4752         
4753         this.fireEvent('click', this, e);
4754     }
4755    
4756 });
4757
4758  
4759
4760  /*
4761  * - LGPL
4762  *
4763  * slider
4764  * 
4765  */
4766
4767
4768 /**
4769  * @class Roo.bootstrap.Slider
4770  * @extends Roo.bootstrap.Component
4771  * Bootstrap Slider class
4772  *    
4773  * @constructor
4774  * Create a new Slider
4775  * @param {Object} config The config object
4776  */
4777
4778 Roo.bootstrap.Slider = function(config){
4779     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4780 };
4781
4782 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4783     
4784     getAutoCreate : function(){
4785         
4786         var cfg = {
4787             tag: 'div',
4788             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4789             cn: [
4790                 {
4791                     tag: 'a',
4792                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4793                 }
4794             ]
4795         }
4796         
4797         return cfg;
4798     }
4799    
4800 });
4801
4802  /*
4803  * Based on:
4804  * Ext JS Library 1.1.1
4805  * Copyright(c) 2006-2007, Ext JS, LLC.
4806  *
4807  * Originally Released Under LGPL - original licence link has changed is not relivant.
4808  *
4809  * Fork - LGPL
4810  * <script type="text/javascript">
4811  */
4812  
4813
4814 /**
4815  * @class Roo.grid.ColumnModel
4816  * @extends Roo.util.Observable
4817  * This is the default implementation of a ColumnModel used by the Grid. It defines
4818  * the columns in the grid.
4819  * <br>Usage:<br>
4820  <pre><code>
4821  var colModel = new Roo.grid.ColumnModel([
4822         {header: "Ticker", width: 60, sortable: true, locked: true},
4823         {header: "Company Name", width: 150, sortable: true},
4824         {header: "Market Cap.", width: 100, sortable: true},
4825         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4826         {header: "Employees", width: 100, sortable: true, resizable: false}
4827  ]);
4828  </code></pre>
4829  * <p>
4830  
4831  * The config options listed for this class are options which may appear in each
4832  * individual column definition.
4833  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4834  * @constructor
4835  * @param {Object} config An Array of column config objects. See this class's
4836  * config objects for details.
4837 */
4838 Roo.grid.ColumnModel = function(config){
4839         /**
4840      * The config passed into the constructor
4841      */
4842     this.config = config;
4843     this.lookup = {};
4844
4845     // if no id, create one
4846     // if the column does not have a dataIndex mapping,
4847     // map it to the order it is in the config
4848     for(var i = 0, len = config.length; i < len; i++){
4849         var c = config[i];
4850         if(typeof c.dataIndex == "undefined"){
4851             c.dataIndex = i;
4852         }
4853         if(typeof c.renderer == "string"){
4854             c.renderer = Roo.util.Format[c.renderer];
4855         }
4856         if(typeof c.id == "undefined"){
4857             c.id = Roo.id();
4858         }
4859         if(c.editor && c.editor.xtype){
4860             c.editor  = Roo.factory(c.editor, Roo.grid);
4861         }
4862         if(c.editor && c.editor.isFormField){
4863             c.editor = new Roo.grid.GridEditor(c.editor);
4864         }
4865         this.lookup[c.id] = c;
4866     }
4867
4868     /**
4869      * The width of columns which have no width specified (defaults to 100)
4870      * @type Number
4871      */
4872     this.defaultWidth = 100;
4873
4874     /**
4875      * Default sortable of columns which have no sortable specified (defaults to false)
4876      * @type Boolean
4877      */
4878     this.defaultSortable = false;
4879
4880     this.addEvents({
4881         /**
4882              * @event widthchange
4883              * Fires when the width of a column changes.
4884              * @param {ColumnModel} this
4885              * @param {Number} columnIndex The column index
4886              * @param {Number} newWidth The new width
4887              */
4888             "widthchange": true,
4889         /**
4890              * @event headerchange
4891              * Fires when the text of a header changes.
4892              * @param {ColumnModel} this
4893              * @param {Number} columnIndex The column index
4894              * @param {Number} newText The new header text
4895              */
4896             "headerchange": true,
4897         /**
4898              * @event hiddenchange
4899              * Fires when a column is hidden or "unhidden".
4900              * @param {ColumnModel} this
4901              * @param {Number} columnIndex The column index
4902              * @param {Boolean} hidden true if hidden, false otherwise
4903              */
4904             "hiddenchange": true,
4905             /**
4906          * @event columnmoved
4907          * Fires when a column is moved.
4908          * @param {ColumnModel} this
4909          * @param {Number} oldIndex
4910          * @param {Number} newIndex
4911          */
4912         "columnmoved" : true,
4913         /**
4914          * @event columlockchange
4915          * Fires when a column's locked state is changed
4916          * @param {ColumnModel} this
4917          * @param {Number} colIndex
4918          * @param {Boolean} locked true if locked
4919          */
4920         "columnlockchange" : true
4921     });
4922     Roo.grid.ColumnModel.superclass.constructor.call(this);
4923 };
4924 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4925     /**
4926      * @cfg {String} header The header text to display in the Grid view.
4927      */
4928     /**
4929      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4930      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4931      * specified, the column's index is used as an index into the Record's data Array.
4932      */
4933     /**
4934      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4935      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4936      */
4937     /**
4938      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4939      * Defaults to the value of the {@link #defaultSortable} property.
4940      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4941      */
4942     /**
4943      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4944      */
4945     /**
4946      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4947      */
4948     /**
4949      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4950      */
4951     /**
4952      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4953      */
4954     /**
4955      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4956      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4957      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4958      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4959      */
4960        /**
4961      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4962      */
4963     /**
4964      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4965      */
4966     /**
4967      * @cfg {String} cursor (Optional)
4968      */
4969     /**
4970      * @cfg {String} tooltip (Optional)
4971      */
4972     /**
4973      * Returns the id of the column at the specified index.
4974      * @param {Number} index The column index
4975      * @return {String} the id
4976      */
4977     getColumnId : function(index){
4978         return this.config[index].id;
4979     },
4980
4981     /**
4982      * Returns the column for a specified id.
4983      * @param {String} id The column id
4984      * @return {Object} the column
4985      */
4986     getColumnById : function(id){
4987         return this.lookup[id];
4988     },
4989
4990     
4991     /**
4992      * Returns the column for a specified dataIndex.
4993      * @param {String} dataIndex The column dataIndex
4994      * @return {Object|Boolean} the column or false if not found
4995      */
4996     getColumnByDataIndex: function(dataIndex){
4997         var index = this.findColumnIndex(dataIndex);
4998         return index > -1 ? this.config[index] : false;
4999     },
5000     
5001     /**
5002      * Returns the index for a specified column id.
5003      * @param {String} id The column id
5004      * @return {Number} the index, or -1 if not found
5005      */
5006     getIndexById : function(id){
5007         for(var i = 0, len = this.config.length; i < len; i++){
5008             if(this.config[i].id == id){
5009                 return i;
5010             }
5011         }
5012         return -1;
5013     },
5014     
5015     /**
5016      * Returns the index for a specified column dataIndex.
5017      * @param {String} dataIndex The column dataIndex
5018      * @return {Number} the index, or -1 if not found
5019      */
5020     
5021     findColumnIndex : function(dataIndex){
5022         for(var i = 0, len = this.config.length; i < len; i++){
5023             if(this.config[i].dataIndex == dataIndex){
5024                 return i;
5025             }
5026         }
5027         return -1;
5028     },
5029     
5030     
5031     moveColumn : function(oldIndex, newIndex){
5032         var c = this.config[oldIndex];
5033         this.config.splice(oldIndex, 1);
5034         this.config.splice(newIndex, 0, c);
5035         this.dataMap = null;
5036         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5037     },
5038
5039     isLocked : function(colIndex){
5040         return this.config[colIndex].locked === true;
5041     },
5042
5043     setLocked : function(colIndex, value, suppressEvent){
5044         if(this.isLocked(colIndex) == value){
5045             return;
5046         }
5047         this.config[colIndex].locked = value;
5048         if(!suppressEvent){
5049             this.fireEvent("columnlockchange", this, colIndex, value);
5050         }
5051     },
5052
5053     getTotalLockedWidth : function(){
5054         var totalWidth = 0;
5055         for(var i = 0; i < this.config.length; i++){
5056             if(this.isLocked(i) && !this.isHidden(i)){
5057                 this.totalWidth += this.getColumnWidth(i);
5058             }
5059         }
5060         return totalWidth;
5061     },
5062
5063     getLockedCount : function(){
5064         for(var i = 0, len = this.config.length; i < len; i++){
5065             if(!this.isLocked(i)){
5066                 return i;
5067             }
5068         }
5069     },
5070
5071     /**
5072      * Returns the number of columns.
5073      * @return {Number}
5074      */
5075     getColumnCount : function(visibleOnly){
5076         if(visibleOnly === true){
5077             var c = 0;
5078             for(var i = 0, len = this.config.length; i < len; i++){
5079                 if(!this.isHidden(i)){
5080                     c++;
5081                 }
5082             }
5083             return c;
5084         }
5085         return this.config.length;
5086     },
5087
5088     /**
5089      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5090      * @param {Function} fn
5091      * @param {Object} scope (optional)
5092      * @return {Array} result
5093      */
5094     getColumnsBy : function(fn, scope){
5095         var r = [];
5096         for(var i = 0, len = this.config.length; i < len; i++){
5097             var c = this.config[i];
5098             if(fn.call(scope||this, c, i) === true){
5099                 r[r.length] = c;
5100             }
5101         }
5102         return r;
5103     },
5104
5105     /**
5106      * Returns true if the specified column is sortable.
5107      * @param {Number} col The column index
5108      * @return {Boolean}
5109      */
5110     isSortable : function(col){
5111         if(typeof this.config[col].sortable == "undefined"){
5112             return this.defaultSortable;
5113         }
5114         return this.config[col].sortable;
5115     },
5116
5117     /**
5118      * Returns the rendering (formatting) function defined for the column.
5119      * @param {Number} col The column index.
5120      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5121      */
5122     getRenderer : function(col){
5123         if(!this.config[col].renderer){
5124             return Roo.grid.ColumnModel.defaultRenderer;
5125         }
5126         return this.config[col].renderer;
5127     },
5128
5129     /**
5130      * Sets the rendering (formatting) function for a column.
5131      * @param {Number} col The column index
5132      * @param {Function} fn The function to use to process the cell's raw data
5133      * to return HTML markup for the grid view. The render function is called with
5134      * the following parameters:<ul>
5135      * <li>Data value.</li>
5136      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5137      * <li>css A CSS style string to apply to the table cell.</li>
5138      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5139      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5140      * <li>Row index</li>
5141      * <li>Column index</li>
5142      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5143      */
5144     setRenderer : function(col, fn){
5145         this.config[col].renderer = fn;
5146     },
5147
5148     /**
5149      * Returns the width for the specified column.
5150      * @param {Number} col The column index
5151      * @return {Number}
5152      */
5153     getColumnWidth : function(col){
5154         return this.config[col].width * 1 || this.defaultWidth;
5155     },
5156
5157     /**
5158      * Sets the width for a column.
5159      * @param {Number} col The column index
5160      * @param {Number} width The new width
5161      */
5162     setColumnWidth : function(col, width, suppressEvent){
5163         this.config[col].width = width;
5164         this.totalWidth = null;
5165         if(!suppressEvent){
5166              this.fireEvent("widthchange", this, col, width);
5167         }
5168     },
5169
5170     /**
5171      * Returns the total width of all columns.
5172      * @param {Boolean} includeHidden True to include hidden column widths
5173      * @return {Number}
5174      */
5175     getTotalWidth : function(includeHidden){
5176         if(!this.totalWidth){
5177             this.totalWidth = 0;
5178             for(var i = 0, len = this.config.length; i < len; i++){
5179                 if(includeHidden || !this.isHidden(i)){
5180                     this.totalWidth += this.getColumnWidth(i);
5181                 }
5182             }
5183         }
5184         return this.totalWidth;
5185     },
5186
5187     /**
5188      * Returns the header for the specified column.
5189      * @param {Number} col The column index
5190      * @return {String}
5191      */
5192     getColumnHeader : function(col){
5193         return this.config[col].header;
5194     },
5195
5196     /**
5197      * Sets the header for a column.
5198      * @param {Number} col The column index
5199      * @param {String} header The new header
5200      */
5201     setColumnHeader : function(col, header){
5202         this.config[col].header = header;
5203         this.fireEvent("headerchange", this, col, header);
5204     },
5205
5206     /**
5207      * Returns the tooltip for the specified column.
5208      * @param {Number} col The column index
5209      * @return {String}
5210      */
5211     getColumnTooltip : function(col){
5212             return this.config[col].tooltip;
5213     },
5214     /**
5215      * Sets the tooltip for a column.
5216      * @param {Number} col The column index
5217      * @param {String} tooltip The new tooltip
5218      */
5219     setColumnTooltip : function(col, tooltip){
5220             this.config[col].tooltip = tooltip;
5221     },
5222
5223     /**
5224      * Returns the dataIndex for the specified column.
5225      * @param {Number} col The column index
5226      * @return {Number}
5227      */
5228     getDataIndex : function(col){
5229         return this.config[col].dataIndex;
5230     },
5231
5232     /**
5233      * Sets the dataIndex for a column.
5234      * @param {Number} col The column index
5235      * @param {Number} dataIndex The new dataIndex
5236      */
5237     setDataIndex : function(col, dataIndex){
5238         this.config[col].dataIndex = dataIndex;
5239     },
5240
5241     
5242     
5243     /**
5244      * Returns true if the cell is editable.
5245      * @param {Number} colIndex The column index
5246      * @param {Number} rowIndex The row index
5247      * @return {Boolean}
5248      */
5249     isCellEditable : function(colIndex, rowIndex){
5250         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5251     },
5252
5253     /**
5254      * Returns the editor defined for the cell/column.
5255      * return false or null to disable editing.
5256      * @param {Number} colIndex The column index
5257      * @param {Number} rowIndex The row index
5258      * @return {Object}
5259      */
5260     getCellEditor : function(colIndex, rowIndex){
5261         return this.config[colIndex].editor;
5262     },
5263
5264     /**
5265      * Sets if a column is editable.
5266      * @param {Number} col The column index
5267      * @param {Boolean} editable True if the column is editable
5268      */
5269     setEditable : function(col, editable){
5270         this.config[col].editable = editable;
5271     },
5272
5273
5274     /**
5275      * Returns true if the column is hidden.
5276      * @param {Number} colIndex The column index
5277      * @return {Boolean}
5278      */
5279     isHidden : function(colIndex){
5280         return this.config[colIndex].hidden;
5281     },
5282
5283
5284     /**
5285      * Returns true if the column width cannot be changed
5286      */
5287     isFixed : function(colIndex){
5288         return this.config[colIndex].fixed;
5289     },
5290
5291     /**
5292      * Returns true if the column can be resized
5293      * @return {Boolean}
5294      */
5295     isResizable : function(colIndex){
5296         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5297     },
5298     /**
5299      * Sets if a column is hidden.
5300      * @param {Number} colIndex The column index
5301      * @param {Boolean} hidden True if the column is hidden
5302      */
5303     setHidden : function(colIndex, hidden){
5304         this.config[colIndex].hidden = hidden;
5305         this.totalWidth = null;
5306         this.fireEvent("hiddenchange", this, colIndex, hidden);
5307     },
5308
5309     /**
5310      * Sets the editor for a column.
5311      * @param {Number} col The column index
5312      * @param {Object} editor The editor object
5313      */
5314     setEditor : function(col, editor){
5315         this.config[col].editor = editor;
5316     }
5317 });
5318
5319 Roo.grid.ColumnModel.defaultRenderer = function(value){
5320         if(typeof value == "string" && value.length < 1){
5321             return "&#160;";
5322         }
5323         return value;
5324 };
5325
5326 // Alias for backwards compatibility
5327 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5328 /*
5329  * Based on:
5330  * Ext JS Library 1.1.1
5331  * Copyright(c) 2006-2007, Ext JS, LLC.
5332  *
5333  * Originally Released Under LGPL - original licence link has changed is not relivant.
5334  *
5335  * Fork - LGPL
5336  * <script type="text/javascript">
5337  */
5338  
5339 /**
5340  * @class Roo.LoadMask
5341  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5342  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5343  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5344  * element's UpdateManager load indicator and will be destroyed after the initial load.
5345  * @constructor
5346  * Create a new LoadMask
5347  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5348  * @param {Object} config The config object
5349  */
5350 Roo.LoadMask = function(el, config){
5351     this.el = Roo.get(el);
5352     Roo.apply(this, config);
5353     if(this.store){
5354         this.store.on('beforeload', this.onBeforeLoad, this);
5355         this.store.on('load', this.onLoad, this);
5356         this.store.on('loadexception', this.onLoadException, this);
5357         this.removeMask = false;
5358     }else{
5359         var um = this.el.getUpdateManager();
5360         um.showLoadIndicator = false; // disable the default indicator
5361         um.on('beforeupdate', this.onBeforeLoad, this);
5362         um.on('update', this.onLoad, this);
5363         um.on('failure', this.onLoad, this);
5364         this.removeMask = true;
5365     }
5366 };
5367
5368 Roo.LoadMask.prototype = {
5369     /**
5370      * @cfg {Boolean} removeMask
5371      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5372      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5373      */
5374     /**
5375      * @cfg {String} msg
5376      * The text to display in a centered loading message box (defaults to 'Loading...')
5377      */
5378     msg : 'Loading...',
5379     /**
5380      * @cfg {String} msgCls
5381      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5382      */
5383     msgCls : 'x-mask-loading',
5384
5385     /**
5386      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5387      * @type Boolean
5388      */
5389     disabled: false,
5390
5391     /**
5392      * Disables the mask to prevent it from being displayed
5393      */
5394     disable : function(){
5395        this.disabled = true;
5396     },
5397
5398     /**
5399      * Enables the mask so that it can be displayed
5400      */
5401     enable : function(){
5402         this.disabled = false;
5403     },
5404     
5405     onLoadException : function()
5406     {
5407         Roo.log(arguments);
5408         
5409         if (typeof(arguments[3]) != 'undefined') {
5410             Roo.MessageBox.alert("Error loading",arguments[3]);
5411         } 
5412         /*
5413         try {
5414             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5415                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5416             }   
5417         } catch(e) {
5418             
5419         }
5420         */
5421     
5422         
5423         
5424         this.el.unmask(this.removeMask);
5425     },
5426     // private
5427     onLoad : function()
5428     {
5429         this.el.unmask(this.removeMask);
5430     },
5431
5432     // private
5433     onBeforeLoad : function(){
5434         if(!this.disabled){
5435             this.el.mask(this.msg, this.msgCls);
5436         }
5437     },
5438
5439     // private
5440     destroy : function(){
5441         if(this.store){
5442             this.store.un('beforeload', this.onBeforeLoad, this);
5443             this.store.un('load', this.onLoad, this);
5444             this.store.un('loadexception', this.onLoadException, this);
5445         }else{
5446             var um = this.el.getUpdateManager();
5447             um.un('beforeupdate', this.onBeforeLoad, this);
5448             um.un('update', this.onLoad, this);
5449             um.un('failure', this.onLoad, this);
5450         }
5451     }
5452 };/*
5453  * - LGPL
5454  *
5455  * table
5456  * 
5457  */
5458
5459 /**
5460  * @class Roo.bootstrap.Table
5461  * @extends Roo.bootstrap.Component
5462  * Bootstrap Table class
5463  * @cfg {String} cls table class
5464  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5465  * @cfg {String} bgcolor Specifies the background color for a table
5466  * @cfg {Number} border Specifies whether the table cells should have borders or not
5467  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5468  * @cfg {Number} cellspacing Specifies the space between cells
5469  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5470  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5471  * @cfg {String} sortable Specifies that the table should be sortable
5472  * @cfg {String} summary Specifies a summary of the content of a table
5473  * @cfg {Number} width Specifies the width of a table
5474  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5475  * 
5476  * @cfg {boolean} striped Should the rows be alternative striped
5477  * @cfg {boolean} bordered Add borders to the table
5478  * @cfg {boolean} hover Add hover highlighting
5479  * @cfg {boolean} condensed Format condensed
5480  * @cfg {boolean} responsive Format condensed
5481  * @cfg {Boolean} loadMask (true|false) default false
5482  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5483  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5484  * @cfg {Boolean} rowSelection (true|false) default false
5485  * @cfg {Boolean} cellSelection (true|false) default false
5486  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5487  
5488  * 
5489  * @constructor
5490  * Create a new Table
5491  * @param {Object} config The config object
5492  */
5493
5494 Roo.bootstrap.Table = function(config){
5495     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5496     
5497     // BC...
5498     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5499     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5500     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5501     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5502     
5503     
5504     if (this.sm) {
5505         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5506         this.sm = this.selModel;
5507         this.sm.xmodule = this.xmodule || false;
5508     }
5509     if (this.cm && typeof(this.cm.config) == 'undefined') {
5510         this.colModel = new Roo.grid.ColumnModel(this.cm);
5511         this.cm = this.colModel;
5512         this.cm.xmodule = this.xmodule || false;
5513     }
5514     if (this.store) {
5515         this.store= Roo.factory(this.store, Roo.data);
5516         this.ds = this.store;
5517         this.ds.xmodule = this.xmodule || false;
5518          
5519     }
5520     if (this.footer && this.store) {
5521         this.footer.dataSource = this.ds;
5522         this.footer = Roo.factory(this.footer);
5523     }
5524     
5525     /** @private */
5526     this.addEvents({
5527         /**
5528          * @event cellclick
5529          * Fires when a cell is clicked
5530          * @param {Roo.bootstrap.Table} this
5531          * @param {Roo.Element} el
5532          * @param {Number} rowIndex
5533          * @param {Number} columnIndex
5534          * @param {Roo.EventObject} e
5535          */
5536         "cellclick" : true,
5537         /**
5538          * @event celldblclick
5539          * Fires when a cell is double clicked
5540          * @param {Roo.bootstrap.Table} this
5541          * @param {Roo.Element} el
5542          * @param {Number} rowIndex
5543          * @param {Number} columnIndex
5544          * @param {Roo.EventObject} e
5545          */
5546         "celldblclick" : true,
5547         /**
5548          * @event rowclick
5549          * Fires when a row is clicked
5550          * @param {Roo.bootstrap.Table} this
5551          * @param {Roo.Element} el
5552          * @param {Number} rowIndex
5553          * @param {Roo.EventObject} e
5554          */
5555         "rowclick" : true,
5556         /**
5557          * @event rowdblclick
5558          * Fires when a row is double clicked
5559          * @param {Roo.bootstrap.Table} this
5560          * @param {Roo.Element} el
5561          * @param {Number} rowIndex
5562          * @param {Roo.EventObject} e
5563          */
5564         "rowdblclick" : true,
5565         /**
5566          * @event mouseover
5567          * Fires when a mouseover occur
5568          * @param {Roo.bootstrap.Table} this
5569          * @param {Roo.Element} el
5570          * @param {Number} rowIndex
5571          * @param {Number} columnIndex
5572          * @param {Roo.EventObject} e
5573          */
5574         "mouseover" : true,
5575         /**
5576          * @event mouseout
5577          * Fires when a mouseout occur
5578          * @param {Roo.bootstrap.Table} this
5579          * @param {Roo.Element} el
5580          * @param {Number} rowIndex
5581          * @param {Number} columnIndex
5582          * @param {Roo.EventObject} e
5583          */
5584         "mouseout" : true,
5585         /**
5586          * @event rowclass
5587          * Fires when a row is rendered, so you can change add a style to it.
5588          * @param {Roo.bootstrap.Table} this
5589          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5590          */
5591         'rowclass' : true,
5592           /**
5593          * @event rowsrendered
5594          * Fires when all the  rows have been rendered
5595          * @param {Roo.bootstrap.Table} this
5596          */
5597         'rowsrendered' : true
5598         
5599     });
5600 };
5601
5602 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5603     
5604     cls: false,
5605     align: false,
5606     bgcolor: false,
5607     border: false,
5608     cellpadding: false,
5609     cellspacing: false,
5610     frame: false,
5611     rules: false,
5612     sortable: false,
5613     summary: false,
5614     width: false,
5615     striped : false,
5616     bordered: false,
5617     hover:  false,
5618     condensed : false,
5619     responsive : false,
5620     sm : false,
5621     cm : false,
5622     store : false,
5623     loadMask : false,
5624     footerShow : true,
5625     headerShow : true,
5626   
5627     rowSelection : false,
5628     cellSelection : false,
5629     layout : false,
5630     
5631     // Roo.Element - the tbody
5632     mainBody: false, 
5633     
5634     getAutoCreate : function(){
5635         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5636         
5637         cfg = {
5638             tag: 'table',
5639             cls : 'table',
5640             cn : []
5641         }
5642             
5643         if (this.striped) {
5644             cfg.cls += ' table-striped';
5645         }
5646         
5647         if (this.hover) {
5648             cfg.cls += ' table-hover';
5649         }
5650         if (this.bordered) {
5651             cfg.cls += ' table-bordered';
5652         }
5653         if (this.condensed) {
5654             cfg.cls += ' table-condensed';
5655         }
5656         if (this.responsive) {
5657             cfg.cls += ' table-responsive';
5658         }
5659         
5660         if (this.cls) {
5661             cfg.cls+=  ' ' +this.cls;
5662         }
5663         
5664         // this lot should be simplifed...
5665         
5666         if (this.align) {
5667             cfg.align=this.align;
5668         }
5669         if (this.bgcolor) {
5670             cfg.bgcolor=this.bgcolor;
5671         }
5672         if (this.border) {
5673             cfg.border=this.border;
5674         }
5675         if (this.cellpadding) {
5676             cfg.cellpadding=this.cellpadding;
5677         }
5678         if (this.cellspacing) {
5679             cfg.cellspacing=this.cellspacing;
5680         }
5681         if (this.frame) {
5682             cfg.frame=this.frame;
5683         }
5684         if (this.rules) {
5685             cfg.rules=this.rules;
5686         }
5687         if (this.sortable) {
5688             cfg.sortable=this.sortable;
5689         }
5690         if (this.summary) {
5691             cfg.summary=this.summary;
5692         }
5693         if (this.width) {
5694             cfg.width=this.width;
5695         }
5696         if (this.layout) {
5697             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5698         }
5699         
5700         if(this.store || this.cm){
5701             if(this.headerShow){
5702                 cfg.cn.push(this.renderHeader());
5703             }
5704             
5705             cfg.cn.push(this.renderBody());
5706             
5707             if(this.footerShow){
5708                 cfg.cn.push(this.renderFooter());
5709             }
5710             
5711             cfg.cls+=  ' TableGrid';
5712         }
5713         
5714         return { cn : [ cfg ] };
5715     },
5716     
5717     initEvents : function()
5718     {   
5719         if(!this.store || !this.cm){
5720             return;
5721         }
5722         
5723         //Roo.log('initEvents with ds!!!!');
5724         
5725         this.mainBody = this.el.select('tbody', true).first();
5726         
5727         
5728         var _this = this;
5729         
5730         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5731             e.on('click', _this.sort, _this);
5732         });
5733         
5734         this.el.on("click", this.onClick, this);
5735         this.el.on("dblclick", this.onDblClick, this);
5736         
5737         // why is this done????? = it breaks dialogs??
5738         //this.parent().el.setStyle('position', 'relative');
5739         
5740         
5741         if (this.footer) {
5742             this.footer.parentId = this.id;
5743             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5744         }
5745         
5746         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5747         
5748         this.store.on('load', this.onLoad, this);
5749         this.store.on('beforeload', this.onBeforeLoad, this);
5750         this.store.on('update', this.onUpdate, this);
5751         this.store.on('add', this.onAdd, this);
5752         
5753     },
5754     
5755     onMouseover : function(e, el)
5756     {
5757         var cell = Roo.get(el);
5758         
5759         if(!cell){
5760             return;
5761         }
5762         
5763         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5764             cell = cell.findParent('td', false, true);
5765         }
5766         
5767         var row = cell.findParent('tr', false, true);
5768         var cellIndex = cell.dom.cellIndex;
5769         var rowIndex = row.dom.rowIndex - 1; // start from 0
5770         
5771         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5772         
5773     },
5774     
5775     onMouseout : function(e, el)
5776     {
5777         var cell = Roo.get(el);
5778         
5779         if(!cell){
5780             return;
5781         }
5782         
5783         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5784             cell = cell.findParent('td', false, true);
5785         }
5786         
5787         var row = cell.findParent('tr', false, true);
5788         var cellIndex = cell.dom.cellIndex;
5789         var rowIndex = row.dom.rowIndex - 1; // start from 0
5790         
5791         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5792         
5793     },
5794     
5795     onClick : function(e, el)
5796     {
5797         var cell = Roo.get(el);
5798         
5799         if(!cell || (!this.cellSelection && !this.rowSelection)){
5800             return;
5801         }
5802         
5803         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5804             cell = cell.findParent('td', false, true);
5805         }
5806         
5807         if(!cell || typeof(cell) == 'undefined'){
5808             return;
5809         }
5810         
5811         var row = cell.findParent('tr', false, true);
5812         
5813         if(!row || typeof(row) == 'undefined'){
5814             return;
5815         }
5816         
5817         var cellIndex = cell.dom.cellIndex;
5818         var rowIndex = this.getRowIndex(row);
5819         
5820         // why??? - should these not be based on SelectionModel?
5821         if(this.cellSelection){
5822             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5823         }
5824         
5825         if(this.rowSelection){
5826             this.fireEvent('rowclick', this, row, rowIndex, e);
5827         }
5828         
5829         
5830     },
5831     
5832     onDblClick : function(e,el)
5833     {
5834         var cell = Roo.get(el);
5835         
5836         if(!cell || (!this.CellSelection && !this.RowSelection)){
5837             return;
5838         }
5839         
5840         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5841             cell = cell.findParent('td', false, true);
5842         }
5843         
5844         if(!cell || typeof(cell) == 'undefined'){
5845             return;
5846         }
5847         
5848         var row = cell.findParent('tr', false, true);
5849         
5850         if(!row || typeof(row) == 'undefined'){
5851             return;
5852         }
5853         
5854         var cellIndex = cell.dom.cellIndex;
5855         var rowIndex = this.getRowIndex(row);
5856         
5857         if(this.CellSelection){
5858             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5859         }
5860         
5861         if(this.RowSelection){
5862             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5863         }
5864     },
5865     
5866     sort : function(e,el)
5867     {
5868         var col = Roo.get(el);
5869         
5870         if(!col.hasClass('sortable')){
5871             return;
5872         }
5873         
5874         var sort = col.attr('sort');
5875         var dir = 'ASC';
5876         
5877         if(col.hasClass('glyphicon-arrow-up')){
5878             dir = 'DESC';
5879         }
5880         
5881         this.store.sortInfo = {field : sort, direction : dir};
5882         
5883         if (this.footer) {
5884             Roo.log("calling footer first");
5885             this.footer.onClick('first');
5886         } else {
5887         
5888             this.store.load({ params : { start : 0 } });
5889         }
5890     },
5891     
5892     renderHeader : function()
5893     {
5894         var header = {
5895             tag: 'thead',
5896             cn : []
5897         };
5898         
5899         var cm = this.cm;
5900         
5901         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5902             
5903             var config = cm.config[i];
5904             
5905             var c = {
5906                 tag: 'th',
5907                 style : '',
5908                 html: cm.getColumnHeader(i)
5909             };
5910             
5911             var hh = '';
5912             
5913             if(typeof(config.lgHeader) != 'undefined'){
5914                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5915             }
5916             
5917             if(typeof(config.mdHeader) != 'undefined'){
5918                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5919             }
5920             
5921             if(typeof(config.smHeader) != 'undefined'){
5922                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5923             }
5924             
5925             if(typeof(config.xsHeader) != 'undefined'){
5926                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5927             }
5928             
5929             if(hh.length){
5930                 c.html = hh;
5931             }
5932             
5933             if(typeof(config.tooltip) != 'undefined'){
5934                 c.tooltip = config.tooltip;
5935             }
5936             
5937             if(typeof(config.colspan) != 'undefined'){
5938                 c.colspan = config.colspan;
5939             }
5940             
5941             if(typeof(config.hidden) != 'undefined' && config.hidden){
5942                 c.style += ' display:none;';
5943             }
5944             
5945             if(typeof(config.dataIndex) != 'undefined'){
5946                 c.sort = config.dataIndex;
5947             }
5948             
5949             if(typeof(config.sortable) != 'undefined' && config.sortable){
5950                 c.cls = 'sortable';
5951             }
5952             
5953             if(typeof(config.align) != 'undefined' && config.align.length){
5954                 c.style += ' text-align:' + config.align + ';';
5955             }
5956             
5957             if(typeof(config.width) != 'undefined'){
5958                 c.style += ' width:' + config.width + 'px;';
5959             }
5960             
5961             if(typeof(config.cls) != 'undefined'){
5962                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5963             }
5964             
5965             header.cn.push(c)
5966         }
5967         
5968         return header;
5969     },
5970     
5971     renderBody : function()
5972     {
5973         var body = {
5974             tag: 'tbody',
5975             cn : [
5976                 {
5977                     tag: 'tr',
5978                     cn : [
5979                         {
5980                             tag : 'td',
5981                             colspan :  this.cm.getColumnCount()
5982                         }
5983                     ]
5984                 }
5985             ]
5986         };
5987         
5988         return body;
5989     },
5990     
5991     renderFooter : function()
5992     {
5993         var footer = {
5994             tag: 'tfoot',
5995             cn : [
5996                 {
5997                     tag: 'tr',
5998                     cn : [
5999                         {
6000                             tag : 'td',
6001                             colspan :  this.cm.getColumnCount()
6002                         }
6003                     ]
6004                 }
6005             ]
6006         };
6007         
6008         return footer;
6009     },
6010     
6011     
6012     
6013     onLoad : function()
6014     {
6015         Roo.log('ds onload');
6016         this.clear();
6017         
6018         var _this = this;
6019         var cm = this.cm;
6020         var ds = this.store;
6021         
6022         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6023             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6024             
6025             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6026                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6027             }
6028             
6029             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6030                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6031             }
6032         });
6033         
6034         var tbody =  this.mainBody;
6035               
6036         if(ds.getCount() > 0){
6037             ds.data.each(function(d,rowIndex){
6038                 var row =  this.renderRow(cm, ds, rowIndex);
6039                 
6040                 tbody.createChild(row);
6041                 
6042                 var _this = this;
6043                 
6044                 if(row.cellObjects.length){
6045                     Roo.each(row.cellObjects, function(r){
6046                         _this.renderCellObject(r);
6047                     })
6048                 }
6049                 
6050             }, this);
6051         }
6052         
6053         Roo.each(this.el.select('tbody td', true).elements, function(e){
6054             e.on('mouseover', _this.onMouseover, _this);
6055         });
6056         
6057         Roo.each(this.el.select('tbody td', true).elements, function(e){
6058             e.on('mouseout', _this.onMouseout, _this);
6059         });
6060         this.fireEvent('rowsrendered', this);
6061         //if(this.loadMask){
6062         //    this.maskEl.hide();
6063         //}
6064     },
6065     
6066     
6067     onUpdate : function(ds,record)
6068     {
6069         this.refreshRow(record);
6070     },
6071     
6072     onRemove : function(ds, record, index, isUpdate){
6073         if(isUpdate !== true){
6074             this.fireEvent("beforerowremoved", this, index, record);
6075         }
6076         var bt = this.mainBody.dom;
6077         
6078         var rows = this.el.select('tbody > tr', true).elements;
6079         
6080         if(typeof(rows[index]) != 'undefined'){
6081             bt.removeChild(rows[index].dom);
6082         }
6083         
6084 //        if(bt.rows[index]){
6085 //            bt.removeChild(bt.rows[index]);
6086 //        }
6087         
6088         if(isUpdate !== true){
6089             //this.stripeRows(index);
6090             //this.syncRowHeights(index, index);
6091             //this.layout();
6092             this.fireEvent("rowremoved", this, index, record);
6093         }
6094     },
6095     
6096     onAdd : function(ds, records, rowIndex)
6097     {
6098         //Roo.log('on Add called');
6099         // - note this does not handle multiple adding very well..
6100         var bt = this.mainBody.dom;
6101         for (var i =0 ; i < records.length;i++) {
6102             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6103             //Roo.log(records[i]);
6104             //Roo.log(this.store.getAt(rowIndex+i));
6105             this.insertRow(this.store, rowIndex + i, false);
6106             return;
6107         }
6108         
6109     },
6110     
6111     
6112     refreshRow : function(record){
6113         var ds = this.store, index;
6114         if(typeof record == 'number'){
6115             index = record;
6116             record = ds.getAt(index);
6117         }else{
6118             index = ds.indexOf(record);
6119         }
6120         this.insertRow(ds, index, true);
6121         this.onRemove(ds, record, index+1, true);
6122         //this.syncRowHeights(index, index);
6123         //this.layout();
6124         this.fireEvent("rowupdated", this, index, record);
6125     },
6126     
6127     insertRow : function(dm, rowIndex, isUpdate){
6128         
6129         if(!isUpdate){
6130             this.fireEvent("beforerowsinserted", this, rowIndex);
6131         }
6132             //var s = this.getScrollState();
6133         var row = this.renderRow(this.cm, this.store, rowIndex);
6134         // insert before rowIndex..
6135         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6136         
6137         var _this = this;
6138                 
6139         if(row.cellObjects.length){
6140             Roo.each(row.cellObjects, function(r){
6141                 _this.renderCellObject(r);
6142             })
6143         }
6144             
6145         if(!isUpdate){
6146             this.fireEvent("rowsinserted", this, rowIndex);
6147             //this.syncRowHeights(firstRow, lastRow);
6148             //this.stripeRows(firstRow);
6149             //this.layout();
6150         }
6151         
6152     },
6153     
6154     
6155     getRowDom : function(rowIndex)
6156     {
6157         var rows = this.el.select('tbody > tr', true).elements;
6158         
6159         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6160         
6161     },
6162     // returns the object tree for a tr..
6163   
6164     
6165     renderRow : function(cm, ds, rowIndex) 
6166     {
6167         
6168         var d = ds.getAt(rowIndex);
6169         
6170         var row = {
6171             tag : 'tr',
6172             cn : []
6173         };
6174             
6175         var cellObjects = [];
6176         
6177         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6178             var config = cm.config[i];
6179             
6180             var renderer = cm.getRenderer(i);
6181             var value = '';
6182             var id = false;
6183             
6184             if(typeof(renderer) !== 'undefined'){
6185                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6186             }
6187             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6188             // and are rendered into the cells after the row is rendered - using the id for the element.
6189             
6190             if(typeof(value) === 'object'){
6191                 id = Roo.id();
6192                 cellObjects.push({
6193                     container : id,
6194                     cfg : value 
6195                 })
6196             }
6197             
6198             var rowcfg = {
6199                 record: d,
6200                 rowIndex : rowIndex,
6201                 colIndex : i,
6202                 rowClass : ''
6203             }
6204
6205             this.fireEvent('rowclass', this, rowcfg);
6206             
6207             var td = {
6208                 tag: 'td',
6209                 cls : rowcfg.rowClass,
6210                 style: '',
6211                 html: (typeof(value) === 'object') ? '' : value
6212             };
6213             
6214             if (id) {
6215                 td.id = id;
6216             }
6217             
6218             if(typeof(config.colspan) != 'undefined'){
6219                 td.colspan = config.colspan;
6220             }
6221             
6222             if(typeof(config.hidden) != 'undefined' && config.hidden){
6223                 td.style += ' display:none;';
6224             }
6225             
6226             if(typeof(config.align) != 'undefined' && config.align.length){
6227                 td.style += ' text-align:' + config.align + ';';
6228             }
6229             
6230             if(typeof(config.width) != 'undefined'){
6231                 td.style += ' width:' +  config.width + 'px;';
6232             }
6233             
6234             if(typeof(config.cursor) != 'undefined'){
6235                 td.style += ' cursor:' +  config.cursor + ';';
6236             }
6237             
6238             if(typeof(config.cls) != 'undefined'){
6239                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6240             }
6241              
6242             row.cn.push(td);
6243            
6244         }
6245         
6246         row.cellObjects = cellObjects;
6247         
6248         return row;
6249           
6250     },
6251     
6252     
6253     
6254     onBeforeLoad : function()
6255     {
6256         //Roo.log('ds onBeforeLoad');
6257         
6258         //this.clear();
6259         
6260         //if(this.loadMask){
6261         //    this.maskEl.show();
6262         //}
6263     },
6264      /**
6265      * Remove all rows
6266      */
6267     clear : function()
6268     {
6269         this.el.select('tbody', true).first().dom.innerHTML = '';
6270     },
6271     /**
6272      * Show or hide a row.
6273      * @param {Number} rowIndex to show or hide
6274      * @param {Boolean} state hide
6275      */
6276     setRowVisibility : function(rowIndex, state)
6277     {
6278         var bt = this.mainBody.dom;
6279         
6280         var rows = this.el.select('tbody > tr', true).elements;
6281         
6282         if(typeof(rows[rowIndex]) == 'undefined'){
6283             return;
6284         }
6285         rows[rowIndex].dom.style.display = state ? '' : 'none';
6286     },
6287     
6288     
6289     getSelectionModel : function(){
6290         if(!this.selModel){
6291             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6292         }
6293         return this.selModel;
6294     },
6295     /*
6296      * Render the Roo.bootstrap object from renderder
6297      */
6298     renderCellObject : function(r)
6299     {
6300         var _this = this;
6301         
6302         var t = r.cfg.render(r.container);
6303         
6304         if(r.cfg.cn){
6305             Roo.each(r.cfg.cn, function(c){
6306                 var child = {
6307                     container: t.getChildContainer(),
6308                     cfg: c
6309                 }
6310                 _this.renderCellObject(child);
6311             })
6312         }
6313     },
6314     
6315     getRowIndex : function(row)
6316     {
6317         var rowIndex = -1;
6318         
6319         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6320             if(el != row){
6321                 return;
6322             }
6323             
6324             rowIndex = index;
6325         });
6326         
6327         return rowIndex;
6328     }
6329    
6330 });
6331
6332  
6333
6334  /*
6335  * - LGPL
6336  *
6337  * table cell
6338  * 
6339  */
6340
6341 /**
6342  * @class Roo.bootstrap.TableCell
6343  * @extends Roo.bootstrap.Component
6344  * Bootstrap TableCell class
6345  * @cfg {String} html cell contain text
6346  * @cfg {String} cls cell class
6347  * @cfg {String} tag cell tag (td|th) default td
6348  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6349  * @cfg {String} align Aligns the content in a cell
6350  * @cfg {String} axis Categorizes cells
6351  * @cfg {String} bgcolor Specifies the background color of a cell
6352  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6353  * @cfg {Number} colspan Specifies the number of columns a cell should span
6354  * @cfg {String} headers Specifies one or more header cells a cell is related to
6355  * @cfg {Number} height Sets the height of a cell
6356  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6357  * @cfg {Number} rowspan Sets the number of rows a cell should span
6358  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6359  * @cfg {String} valign Vertical aligns the content in a cell
6360  * @cfg {Number} width Specifies the width of a cell
6361  * 
6362  * @constructor
6363  * Create a new TableCell
6364  * @param {Object} config The config object
6365  */
6366
6367 Roo.bootstrap.TableCell = function(config){
6368     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6369 };
6370
6371 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6372     
6373     html: false,
6374     cls: false,
6375     tag: false,
6376     abbr: false,
6377     align: false,
6378     axis: false,
6379     bgcolor: false,
6380     charoff: false,
6381     colspan: false,
6382     headers: false,
6383     height: false,
6384     nowrap: false,
6385     rowspan: false,
6386     scope: false,
6387     valign: false,
6388     width: false,
6389     
6390     
6391     getAutoCreate : function(){
6392         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6393         
6394         cfg = {
6395             tag: 'td'
6396         }
6397         
6398         if(this.tag){
6399             cfg.tag = this.tag;
6400         }
6401         
6402         if (this.html) {
6403             cfg.html=this.html
6404         }
6405         if (this.cls) {
6406             cfg.cls=this.cls
6407         }
6408         if (this.abbr) {
6409             cfg.abbr=this.abbr
6410         }
6411         if (this.align) {
6412             cfg.align=this.align
6413         }
6414         if (this.axis) {
6415             cfg.axis=this.axis
6416         }
6417         if (this.bgcolor) {
6418             cfg.bgcolor=this.bgcolor
6419         }
6420         if (this.charoff) {
6421             cfg.charoff=this.charoff
6422         }
6423         if (this.colspan) {
6424             cfg.colspan=this.colspan
6425         }
6426         if (this.headers) {
6427             cfg.headers=this.headers
6428         }
6429         if (this.height) {
6430             cfg.height=this.height
6431         }
6432         if (this.nowrap) {
6433             cfg.nowrap=this.nowrap
6434         }
6435         if (this.rowspan) {
6436             cfg.rowspan=this.rowspan
6437         }
6438         if (this.scope) {
6439             cfg.scope=this.scope
6440         }
6441         if (this.valign) {
6442             cfg.valign=this.valign
6443         }
6444         if (this.width) {
6445             cfg.width=this.width
6446         }
6447         
6448         
6449         return cfg;
6450     }
6451    
6452 });
6453
6454  
6455
6456  /*
6457  * - LGPL
6458  *
6459  * table row
6460  * 
6461  */
6462
6463 /**
6464  * @class Roo.bootstrap.TableRow
6465  * @extends Roo.bootstrap.Component
6466  * Bootstrap TableRow class
6467  * @cfg {String} cls row class
6468  * @cfg {String} align Aligns the content in a table row
6469  * @cfg {String} bgcolor Specifies a background color for a table row
6470  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6471  * @cfg {String} valign Vertical aligns the content in a table row
6472  * 
6473  * @constructor
6474  * Create a new TableRow
6475  * @param {Object} config The config object
6476  */
6477
6478 Roo.bootstrap.TableRow = function(config){
6479     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6480 };
6481
6482 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6483     
6484     cls: false,
6485     align: false,
6486     bgcolor: false,
6487     charoff: false,
6488     valign: false,
6489     
6490     getAutoCreate : function(){
6491         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6492         
6493         cfg = {
6494             tag: 'tr'
6495         }
6496             
6497         if(this.cls){
6498             cfg.cls = this.cls;
6499         }
6500         if(this.align){
6501             cfg.align = this.align;
6502         }
6503         if(this.bgcolor){
6504             cfg.bgcolor = this.bgcolor;
6505         }
6506         if(this.charoff){
6507             cfg.charoff = this.charoff;
6508         }
6509         if(this.valign){
6510             cfg.valign = this.valign;
6511         }
6512         
6513         return cfg;
6514     }
6515    
6516 });
6517
6518  
6519
6520  /*
6521  * - LGPL
6522  *
6523  * table body
6524  * 
6525  */
6526
6527 /**
6528  * @class Roo.bootstrap.TableBody
6529  * @extends Roo.bootstrap.Component
6530  * Bootstrap TableBody class
6531  * @cfg {String} cls element class
6532  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6533  * @cfg {String} align Aligns the content inside the element
6534  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6535  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6536  * 
6537  * @constructor
6538  * Create a new TableBody
6539  * @param {Object} config The config object
6540  */
6541
6542 Roo.bootstrap.TableBody = function(config){
6543     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6544 };
6545
6546 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6547     
6548     cls: false,
6549     tag: false,
6550     align: false,
6551     charoff: false,
6552     valign: false,
6553     
6554     getAutoCreate : function(){
6555         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6556         
6557         cfg = {
6558             tag: 'tbody'
6559         }
6560             
6561         if (this.cls) {
6562             cfg.cls=this.cls
6563         }
6564         if(this.tag){
6565             cfg.tag = this.tag;
6566         }
6567         
6568         if(this.align){
6569             cfg.align = this.align;
6570         }
6571         if(this.charoff){
6572             cfg.charoff = this.charoff;
6573         }
6574         if(this.valign){
6575             cfg.valign = this.valign;
6576         }
6577         
6578         return cfg;
6579     }
6580     
6581     
6582 //    initEvents : function()
6583 //    {
6584 //        
6585 //        if(!this.store){
6586 //            return;
6587 //        }
6588 //        
6589 //        this.store = Roo.factory(this.store, Roo.data);
6590 //        this.store.on('load', this.onLoad, this);
6591 //        
6592 //        this.store.load();
6593 //        
6594 //    },
6595 //    
6596 //    onLoad: function () 
6597 //    {   
6598 //        this.fireEvent('load', this);
6599 //    }
6600 //    
6601 //   
6602 });
6603
6604  
6605
6606  /*
6607  * Based on:
6608  * Ext JS Library 1.1.1
6609  * Copyright(c) 2006-2007, Ext JS, LLC.
6610  *
6611  * Originally Released Under LGPL - original licence link has changed is not relivant.
6612  *
6613  * Fork - LGPL
6614  * <script type="text/javascript">
6615  */
6616
6617 // as we use this in bootstrap.
6618 Roo.namespace('Roo.form');
6619  /**
6620  * @class Roo.form.Action
6621  * Internal Class used to handle form actions
6622  * @constructor
6623  * @param {Roo.form.BasicForm} el The form element or its id
6624  * @param {Object} config Configuration options
6625  */
6626
6627  
6628  
6629 // define the action interface
6630 Roo.form.Action = function(form, options){
6631     this.form = form;
6632     this.options = options || {};
6633 };
6634 /**
6635  * Client Validation Failed
6636  * @const 
6637  */
6638 Roo.form.Action.CLIENT_INVALID = 'client';
6639 /**
6640  * Server Validation Failed
6641  * @const 
6642  */
6643 Roo.form.Action.SERVER_INVALID = 'server';
6644  /**
6645  * Connect to Server Failed
6646  * @const 
6647  */
6648 Roo.form.Action.CONNECT_FAILURE = 'connect';
6649 /**
6650  * Reading Data from Server Failed
6651  * @const 
6652  */
6653 Roo.form.Action.LOAD_FAILURE = 'load';
6654
6655 Roo.form.Action.prototype = {
6656     type : 'default',
6657     failureType : undefined,
6658     response : undefined,
6659     result : undefined,
6660
6661     // interface method
6662     run : function(options){
6663
6664     },
6665
6666     // interface method
6667     success : function(response){
6668
6669     },
6670
6671     // interface method
6672     handleResponse : function(response){
6673
6674     },
6675
6676     // default connection failure
6677     failure : function(response){
6678         
6679         this.response = response;
6680         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6681         this.form.afterAction(this, false);
6682     },
6683
6684     processResponse : function(response){
6685         this.response = response;
6686         if(!response.responseText){
6687             return true;
6688         }
6689         this.result = this.handleResponse(response);
6690         return this.result;
6691     },
6692
6693     // utility functions used internally
6694     getUrl : function(appendParams){
6695         var url = this.options.url || this.form.url || this.form.el.dom.action;
6696         if(appendParams){
6697             var p = this.getParams();
6698             if(p){
6699                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6700             }
6701         }
6702         return url;
6703     },
6704
6705     getMethod : function(){
6706         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6707     },
6708
6709     getParams : function(){
6710         var bp = this.form.baseParams;
6711         var p = this.options.params;
6712         if(p){
6713             if(typeof p == "object"){
6714                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6715             }else if(typeof p == 'string' && bp){
6716                 p += '&' + Roo.urlEncode(bp);
6717             }
6718         }else if(bp){
6719             p = Roo.urlEncode(bp);
6720         }
6721         return p;
6722     },
6723
6724     createCallback : function(){
6725         return {
6726             success: this.success,
6727             failure: this.failure,
6728             scope: this,
6729             timeout: (this.form.timeout*1000),
6730             upload: this.form.fileUpload ? this.success : undefined
6731         };
6732     }
6733 };
6734
6735 Roo.form.Action.Submit = function(form, options){
6736     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6737 };
6738
6739 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6740     type : 'submit',
6741
6742     haveProgress : false,
6743     uploadComplete : false,
6744     
6745     // uploadProgress indicator.
6746     uploadProgress : function()
6747     {
6748         if (!this.form.progressUrl) {
6749             return;
6750         }
6751         
6752         if (!this.haveProgress) {
6753             Roo.MessageBox.progress("Uploading", "Uploading");
6754         }
6755         if (this.uploadComplete) {
6756            Roo.MessageBox.hide();
6757            return;
6758         }
6759         
6760         this.haveProgress = true;
6761    
6762         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6763         
6764         var c = new Roo.data.Connection();
6765         c.request({
6766             url : this.form.progressUrl,
6767             params: {
6768                 id : uid
6769             },
6770             method: 'GET',
6771             success : function(req){
6772                //console.log(data);
6773                 var rdata = false;
6774                 var edata;
6775                 try  {
6776                    rdata = Roo.decode(req.responseText)
6777                 } catch (e) {
6778                     Roo.log("Invalid data from server..");
6779                     Roo.log(edata);
6780                     return;
6781                 }
6782                 if (!rdata || !rdata.success) {
6783                     Roo.log(rdata);
6784                     Roo.MessageBox.alert(Roo.encode(rdata));
6785                     return;
6786                 }
6787                 var data = rdata.data;
6788                 
6789                 if (this.uploadComplete) {
6790                    Roo.MessageBox.hide();
6791                    return;
6792                 }
6793                    
6794                 if (data){
6795                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6796                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6797                     );
6798                 }
6799                 this.uploadProgress.defer(2000,this);
6800             },
6801        
6802             failure: function(data) {
6803                 Roo.log('progress url failed ');
6804                 Roo.log(data);
6805             },
6806             scope : this
6807         });
6808            
6809     },
6810     
6811     
6812     run : function()
6813     {
6814         // run get Values on the form, so it syncs any secondary forms.
6815         this.form.getValues();
6816         
6817         var o = this.options;
6818         var method = this.getMethod();
6819         var isPost = method == 'POST';
6820         if(o.clientValidation === false || this.form.isValid()){
6821             
6822             if (this.form.progressUrl) {
6823                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6824                     (new Date() * 1) + '' + Math.random());
6825                     
6826             } 
6827             
6828             
6829             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6830                 form:this.form.el.dom,
6831                 url:this.getUrl(!isPost),
6832                 method: method,
6833                 params:isPost ? this.getParams() : null,
6834                 isUpload: this.form.fileUpload
6835             }));
6836             
6837             this.uploadProgress();
6838
6839         }else if (o.clientValidation !== false){ // client validation failed
6840             this.failureType = Roo.form.Action.CLIENT_INVALID;
6841             this.form.afterAction(this, false);
6842         }
6843     },
6844
6845     success : function(response)
6846     {
6847         this.uploadComplete= true;
6848         if (this.haveProgress) {
6849             Roo.MessageBox.hide();
6850         }
6851         
6852         
6853         var result = this.processResponse(response);
6854         if(result === true || result.success){
6855             this.form.afterAction(this, true);
6856             return;
6857         }
6858         if(result.errors){
6859             this.form.markInvalid(result.errors);
6860             this.failureType = Roo.form.Action.SERVER_INVALID;
6861         }
6862         this.form.afterAction(this, false);
6863     },
6864     failure : function(response)
6865     {
6866         this.uploadComplete= true;
6867         if (this.haveProgress) {
6868             Roo.MessageBox.hide();
6869         }
6870         
6871         this.response = response;
6872         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6873         this.form.afterAction(this, false);
6874     },
6875     
6876     handleResponse : function(response){
6877         if(this.form.errorReader){
6878             var rs = this.form.errorReader.read(response);
6879             var errors = [];
6880             if(rs.records){
6881                 for(var i = 0, len = rs.records.length; i < len; i++) {
6882                     var r = rs.records[i];
6883                     errors[i] = r.data;
6884                 }
6885             }
6886             if(errors.length < 1){
6887                 errors = null;
6888             }
6889             return {
6890                 success : rs.success,
6891                 errors : errors
6892             };
6893         }
6894         var ret = false;
6895         try {
6896             ret = Roo.decode(response.responseText);
6897         } catch (e) {
6898             ret = {
6899                 success: false,
6900                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6901                 errors : []
6902             };
6903         }
6904         return ret;
6905         
6906     }
6907 });
6908
6909
6910 Roo.form.Action.Load = function(form, options){
6911     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6912     this.reader = this.form.reader;
6913 };
6914
6915 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6916     type : 'load',
6917
6918     run : function(){
6919         
6920         Roo.Ajax.request(Roo.apply(
6921                 this.createCallback(), {
6922                     method:this.getMethod(),
6923                     url:this.getUrl(false),
6924                     params:this.getParams()
6925         }));
6926     },
6927
6928     success : function(response){
6929         
6930         var result = this.processResponse(response);
6931         if(result === true || !result.success || !result.data){
6932             this.failureType = Roo.form.Action.LOAD_FAILURE;
6933             this.form.afterAction(this, false);
6934             return;
6935         }
6936         this.form.clearInvalid();
6937         this.form.setValues(result.data);
6938         this.form.afterAction(this, true);
6939     },
6940
6941     handleResponse : function(response){
6942         if(this.form.reader){
6943             var rs = this.form.reader.read(response);
6944             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6945             return {
6946                 success : rs.success,
6947                 data : data
6948             };
6949         }
6950         return Roo.decode(response.responseText);
6951     }
6952 });
6953
6954 Roo.form.Action.ACTION_TYPES = {
6955     'load' : Roo.form.Action.Load,
6956     'submit' : Roo.form.Action.Submit
6957 };/*
6958  * - LGPL
6959  *
6960  * form
6961  * 
6962  */
6963
6964 /**
6965  * @class Roo.bootstrap.Form
6966  * @extends Roo.bootstrap.Component
6967  * Bootstrap Form class
6968  * @cfg {String} method  GET | POST (default POST)
6969  * @cfg {String} labelAlign top | left (default top)
6970  * @cfg {String} align left  | right - for navbars
6971  * @cfg {Boolean} loadMask load mask when submit (default true)
6972
6973  * 
6974  * @constructor
6975  * Create a new Form
6976  * @param {Object} config The config object
6977  */
6978
6979
6980 Roo.bootstrap.Form = function(config){
6981     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6982     this.addEvents({
6983         /**
6984          * @event clientvalidation
6985          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6986          * @param {Form} this
6987          * @param {Boolean} valid true if the form has passed client-side validation
6988          */
6989         clientvalidation: true,
6990         /**
6991          * @event beforeaction
6992          * Fires before any action is performed. Return false to cancel the action.
6993          * @param {Form} this
6994          * @param {Action} action The action to be performed
6995          */
6996         beforeaction: true,
6997         /**
6998          * @event actionfailed
6999          * Fires when an action fails.
7000          * @param {Form} this
7001          * @param {Action} action The action that failed
7002          */
7003         actionfailed : true,
7004         /**
7005          * @event actioncomplete
7006          * Fires when an action is completed.
7007          * @param {Form} this
7008          * @param {Action} action The action that completed
7009          */
7010         actioncomplete : true
7011     });
7012     
7013 };
7014
7015 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7016       
7017      /**
7018      * @cfg {String} method
7019      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7020      */
7021     method : 'POST',
7022     /**
7023      * @cfg {String} url
7024      * The URL to use for form actions if one isn't supplied in the action options.
7025      */
7026     /**
7027      * @cfg {Boolean} fileUpload
7028      * Set to true if this form is a file upload.
7029      */
7030      
7031     /**
7032      * @cfg {Object} baseParams
7033      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7034      */
7035       
7036     /**
7037      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7038      */
7039     timeout: 30,
7040     /**
7041      * @cfg {Sting} align (left|right) for navbar forms
7042      */
7043     align : 'left',
7044
7045     // private
7046     activeAction : null,
7047  
7048     /**
7049      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7050      * element by passing it or its id or mask the form itself by passing in true.
7051      * @type Mixed
7052      */
7053     waitMsgTarget : false,
7054     
7055     loadMask : true,
7056     
7057     getAutoCreate : function(){
7058         
7059         var cfg = {
7060             tag: 'form',
7061             method : this.method || 'POST',
7062             id : this.id || Roo.id(),
7063             cls : ''
7064         }
7065         if (this.parent().xtype.match(/^Nav/)) {
7066             cfg.cls = 'navbar-form navbar-' + this.align;
7067             
7068         }
7069         
7070         if (this.labelAlign == 'left' ) {
7071             cfg.cls += ' form-horizontal';
7072         }
7073         
7074         
7075         return cfg;
7076     },
7077     initEvents : function()
7078     {
7079         this.el.on('submit', this.onSubmit, this);
7080         // this was added as random key presses on the form where triggering form submit.
7081         this.el.on('keypress', function(e) {
7082             if (e.getCharCode() != 13) {
7083                 return true;
7084             }
7085             // we might need to allow it for textareas.. and some other items.
7086             // check e.getTarget().
7087             
7088             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7089                 return true;
7090             }
7091         
7092             Roo.log("keypress blocked");
7093             
7094             e.preventDefault();
7095             return false;
7096         });
7097         
7098     },
7099     // private
7100     onSubmit : function(e){
7101         e.stopEvent();
7102     },
7103     
7104      /**
7105      * Returns true if client-side validation on the form is successful.
7106      * @return Boolean
7107      */
7108     isValid : function(){
7109         var items = this.getItems();
7110         var valid = true;
7111         items.each(function(f){
7112            if(!f.validate()){
7113                valid = false;
7114                
7115            }
7116         });
7117         return valid;
7118     },
7119     /**
7120      * Returns true if any fields in this form have changed since their original load.
7121      * @return Boolean
7122      */
7123     isDirty : function(){
7124         var dirty = false;
7125         var items = this.getItems();
7126         items.each(function(f){
7127            if(f.isDirty()){
7128                dirty = true;
7129                return false;
7130            }
7131            return true;
7132         });
7133         return dirty;
7134     },
7135      /**
7136      * Performs a predefined action (submit or load) or custom actions you define on this form.
7137      * @param {String} actionName The name of the action type
7138      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7139      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7140      * accept other config options):
7141      * <pre>
7142 Property          Type             Description
7143 ----------------  ---------------  ----------------------------------------------------------------------------------
7144 url               String           The url for the action (defaults to the form's url)
7145 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7146 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7147 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7148                                    validate the form on the client (defaults to false)
7149      * </pre>
7150      * @return {BasicForm} this
7151      */
7152     doAction : function(action, options){
7153         if(typeof action == 'string'){
7154             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7155         }
7156         if(this.fireEvent('beforeaction', this, action) !== false){
7157             this.beforeAction(action);
7158             action.run.defer(100, action);
7159         }
7160         return this;
7161     },
7162     
7163     // private
7164     beforeAction : function(action){
7165         var o = action.options;
7166         
7167         if(this.loadMask){
7168             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7169         }
7170         // not really supported yet.. ??
7171         
7172         //if(this.waitMsgTarget === true){
7173         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7174         //}else if(this.waitMsgTarget){
7175         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7176         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7177         //}else {
7178         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7179        // }
7180          
7181     },
7182
7183     // private
7184     afterAction : function(action, success){
7185         this.activeAction = null;
7186         var o = action.options;
7187         
7188         //if(this.waitMsgTarget === true){
7189             this.el.unmask();
7190         //}else if(this.waitMsgTarget){
7191         //    this.waitMsgTarget.unmask();
7192         //}else{
7193         //    Roo.MessageBox.updateProgress(1);
7194         //    Roo.MessageBox.hide();
7195        // }
7196         // 
7197         if(success){
7198             if(o.reset){
7199                 this.reset();
7200             }
7201             Roo.callback(o.success, o.scope, [this, action]);
7202             this.fireEvent('actioncomplete', this, action);
7203             
7204         }else{
7205             
7206             // failure condition..
7207             // we have a scenario where updates need confirming.
7208             // eg. if a locking scenario exists..
7209             // we look for { errors : { needs_confirm : true }} in the response.
7210             if (
7211                 (typeof(action.result) != 'undefined')  &&
7212                 (typeof(action.result.errors) != 'undefined')  &&
7213                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7214            ){
7215                 var _t = this;
7216                 Roo.log("not supported yet");
7217                  /*
7218                 
7219                 Roo.MessageBox.confirm(
7220                     "Change requires confirmation",
7221                     action.result.errorMsg,
7222                     function(r) {
7223                         if (r != 'yes') {
7224                             return;
7225                         }
7226                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7227                     }
7228                     
7229                 );
7230                 */
7231                 
7232                 
7233                 return;
7234             }
7235             
7236             Roo.callback(o.failure, o.scope, [this, action]);
7237             // show an error message if no failed handler is set..
7238             if (!this.hasListener('actionfailed')) {
7239                 Roo.log("need to add dialog support");
7240                 /*
7241                 Roo.MessageBox.alert("Error",
7242                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7243                         action.result.errorMsg :
7244                         "Saving Failed, please check your entries or try again"
7245                 );
7246                 */
7247             }
7248             
7249             this.fireEvent('actionfailed', this, action);
7250         }
7251         
7252     },
7253     /**
7254      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7255      * @param {String} id The value to search for
7256      * @return Field
7257      */
7258     findField : function(id){
7259         var items = this.getItems();
7260         var field = items.get(id);
7261         if(!field){
7262              items.each(function(f){
7263                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7264                     field = f;
7265                     return false;
7266                 }
7267                 return true;
7268             });
7269         }
7270         return field || null;
7271     },
7272      /**
7273      * Mark fields in this form invalid in bulk.
7274      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7275      * @return {BasicForm} this
7276      */
7277     markInvalid : function(errors){
7278         if(errors instanceof Array){
7279             for(var i = 0, len = errors.length; i < len; i++){
7280                 var fieldError = errors[i];
7281                 var f = this.findField(fieldError.id);
7282                 if(f){
7283                     f.markInvalid(fieldError.msg);
7284                 }
7285             }
7286         }else{
7287             var field, id;
7288             for(id in errors){
7289                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7290                     field.markInvalid(errors[id]);
7291                 }
7292             }
7293         }
7294         //Roo.each(this.childForms || [], function (f) {
7295         //    f.markInvalid(errors);
7296         //});
7297         
7298         return this;
7299     },
7300
7301     /**
7302      * Set values for fields in this form in bulk.
7303      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7304      * @return {BasicForm} this
7305      */
7306     setValues : function(values){
7307         if(values instanceof Array){ // array of objects
7308             for(var i = 0, len = values.length; i < len; i++){
7309                 var v = values[i];
7310                 var f = this.findField(v.id);
7311                 if(f){
7312                     f.setValue(v.value);
7313                     if(this.trackResetOnLoad){
7314                         f.originalValue = f.getValue();
7315                     }
7316                 }
7317             }
7318         }else{ // object hash
7319             var field, id;
7320             for(id in values){
7321                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7322                     
7323                     if (field.setFromData && 
7324                         field.valueField && 
7325                         field.displayField &&
7326                         // combos' with local stores can 
7327                         // be queried via setValue()
7328                         // to set their value..
7329                         (field.store && !field.store.isLocal)
7330                         ) {
7331                         // it's a combo
7332                         var sd = { };
7333                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7334                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7335                         field.setFromData(sd);
7336                         
7337                     } else {
7338                         field.setValue(values[id]);
7339                     }
7340                     
7341                     
7342                     if(this.trackResetOnLoad){
7343                         field.originalValue = field.getValue();
7344                     }
7345                 }
7346             }
7347         }
7348          
7349         //Roo.each(this.childForms || [], function (f) {
7350         //    f.setValues(values);
7351         //});
7352                 
7353         return this;
7354     },
7355
7356     /**
7357      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7358      * they are returned as an array.
7359      * @param {Boolean} asString
7360      * @return {Object}
7361      */
7362     getValues : function(asString){
7363         //if (this.childForms) {
7364             // copy values from the child forms
7365         //    Roo.each(this.childForms, function (f) {
7366         //        this.setValues(f.getValues());
7367         //    }, this);
7368         //}
7369         
7370         
7371         
7372         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7373         if(asString === true){
7374             return fs;
7375         }
7376         return Roo.urlDecode(fs);
7377     },
7378     
7379     /**
7380      * Returns the fields in this form as an object with key/value pairs. 
7381      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7382      * @return {Object}
7383      */
7384     getFieldValues : function(with_hidden)
7385     {
7386         var items = this.getItems();
7387         var ret = {};
7388         items.each(function(f){
7389             if (!f.getName()) {
7390                 return;
7391             }
7392             var v = f.getValue();
7393             if (f.inputType =='radio') {
7394                 if (typeof(ret[f.getName()]) == 'undefined') {
7395                     ret[f.getName()] = ''; // empty..
7396                 }
7397                 
7398                 if (!f.el.dom.checked) {
7399                     return;
7400                     
7401                 }
7402                 v = f.el.dom.value;
7403                 
7404             }
7405             
7406             // not sure if this supported any more..
7407             if ((typeof(v) == 'object') && f.getRawValue) {
7408                 v = f.getRawValue() ; // dates..
7409             }
7410             // combo boxes where name != hiddenName...
7411             if (f.name != f.getName()) {
7412                 ret[f.name] = f.getRawValue();
7413             }
7414             ret[f.getName()] = v;
7415         });
7416         
7417         return ret;
7418     },
7419
7420     /**
7421      * Clears all invalid messages in this form.
7422      * @return {BasicForm} this
7423      */
7424     clearInvalid : function(){
7425         var items = this.getItems();
7426         
7427         items.each(function(f){
7428            f.clearInvalid();
7429         });
7430         
7431         
7432         
7433         return this;
7434     },
7435
7436     /**
7437      * Resets this form.
7438      * @return {BasicForm} this
7439      */
7440     reset : function(){
7441         var items = this.getItems();
7442         items.each(function(f){
7443             f.reset();
7444         });
7445         
7446         Roo.each(this.childForms || [], function (f) {
7447             f.reset();
7448         });
7449        
7450         
7451         return this;
7452     },
7453     getItems : function()
7454     {
7455         var r=new Roo.util.MixedCollection(false, function(o){
7456             return o.id || (o.id = Roo.id());
7457         });
7458         var iter = function(el) {
7459             if (el.inputEl) {
7460                 r.add(el);
7461             }
7462             if (!el.items) {
7463                 return;
7464             }
7465             Roo.each(el.items,function(e) {
7466                 iter(e);
7467             });
7468             
7469             
7470         };
7471         
7472         iter(this);
7473         return r;
7474         
7475         
7476         
7477         
7478     }
7479     
7480 });
7481
7482  
7483 /*
7484  * Based on:
7485  * Ext JS Library 1.1.1
7486  * Copyright(c) 2006-2007, Ext JS, LLC.
7487  *
7488  * Originally Released Under LGPL - original licence link has changed is not relivant.
7489  *
7490  * Fork - LGPL
7491  * <script type="text/javascript">
7492  */
7493 /**
7494  * @class Roo.form.VTypes
7495  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7496  * @singleton
7497  */
7498 Roo.form.VTypes = function(){
7499     // closure these in so they are only created once.
7500     var alpha = /^[a-zA-Z_]+$/;
7501     var alphanum = /^[a-zA-Z0-9_]+$/;
7502     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7503     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7504
7505     // All these messages and functions are configurable
7506     return {
7507         /**
7508          * The function used to validate email addresses
7509          * @param {String} value The email address
7510          */
7511         'email' : function(v){
7512             return email.test(v);
7513         },
7514         /**
7515          * The error text to display when the email validation function returns false
7516          * @type String
7517          */
7518         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7519         /**
7520          * The keystroke filter mask to be applied on email input
7521          * @type RegExp
7522          */
7523         'emailMask' : /[a-z0-9_\.\-@]/i,
7524
7525         /**
7526          * The function used to validate URLs
7527          * @param {String} value The URL
7528          */
7529         'url' : function(v){
7530             return url.test(v);
7531         },
7532         /**
7533          * The error text to display when the url validation function returns false
7534          * @type String
7535          */
7536         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7537         
7538         /**
7539          * The function used to validate alpha values
7540          * @param {String} value The value
7541          */
7542         'alpha' : function(v){
7543             return alpha.test(v);
7544         },
7545         /**
7546          * The error text to display when the alpha validation function returns false
7547          * @type String
7548          */
7549         'alphaText' : 'This field should only contain letters and _',
7550         /**
7551          * The keystroke filter mask to be applied on alpha input
7552          * @type RegExp
7553          */
7554         'alphaMask' : /[a-z_]/i,
7555
7556         /**
7557          * The function used to validate alphanumeric values
7558          * @param {String} value The value
7559          */
7560         'alphanum' : function(v){
7561             return alphanum.test(v);
7562         },
7563         /**
7564          * The error text to display when the alphanumeric validation function returns false
7565          * @type String
7566          */
7567         'alphanumText' : 'This field should only contain letters, numbers and _',
7568         /**
7569          * The keystroke filter mask to be applied on alphanumeric input
7570          * @type RegExp
7571          */
7572         'alphanumMask' : /[a-z0-9_]/i
7573     };
7574 }();/*
7575  * - LGPL
7576  *
7577  * Input
7578  * 
7579  */
7580
7581 /**
7582  * @class Roo.bootstrap.Input
7583  * @extends Roo.bootstrap.Component
7584  * Bootstrap Input class
7585  * @cfg {Boolean} disabled is it disabled
7586  * @cfg {String} fieldLabel - the label associated
7587  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7588  * @cfg {String} name name of the input
7589  * @cfg {string} fieldLabel - the label associated
7590  * @cfg {string}  inputType - input / file submit ...
7591  * @cfg {string} placeholder - placeholder to put in text.
7592  * @cfg {string}  before - input group add on before
7593  * @cfg {string} after - input group add on after
7594  * @cfg {string} size - (lg|sm) or leave empty..
7595  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7596  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7597  * @cfg {Number} md colspan out of 12 for computer-sized screens
7598  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7599  * @cfg {string} value default value of the input
7600  * @cfg {Number} labelWidth set the width of label (0-12)
7601  * @cfg {String} labelAlign (top|left)
7602  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7603  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7604
7605  * @cfg {String} align (left|center|right) Default left
7606  * @cfg {Boolean} forceFeedback (true|false) Default false
7607  * 
7608  * 
7609  * 
7610  * 
7611  * @constructor
7612  * Create a new Input
7613  * @param {Object} config The config object
7614  */
7615
7616 Roo.bootstrap.Input = function(config){
7617     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7618    
7619         this.addEvents({
7620             /**
7621              * @event focus
7622              * Fires when this field receives input focus.
7623              * @param {Roo.form.Field} this
7624              */
7625             focus : true,
7626             /**
7627              * @event blur
7628              * Fires when this field loses input focus.
7629              * @param {Roo.form.Field} this
7630              */
7631             blur : true,
7632             /**
7633              * @event specialkey
7634              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7635              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7636              * @param {Roo.form.Field} this
7637              * @param {Roo.EventObject} e The event object
7638              */
7639             specialkey : true,
7640             /**
7641              * @event change
7642              * Fires just before the field blurs if the field value has changed.
7643              * @param {Roo.form.Field} this
7644              * @param {Mixed} newValue The new value
7645              * @param {Mixed} oldValue The original value
7646              */
7647             change : true,
7648             /**
7649              * @event invalid
7650              * Fires after the field has been marked as invalid.
7651              * @param {Roo.form.Field} this
7652              * @param {String} msg The validation message
7653              */
7654             invalid : true,
7655             /**
7656              * @event valid
7657              * Fires after the field has been validated with no errors.
7658              * @param {Roo.form.Field} this
7659              */
7660             valid : true,
7661              /**
7662              * @event keyup
7663              * Fires after the key up
7664              * @param {Roo.form.Field} this
7665              * @param {Roo.EventObject}  e The event Object
7666              */
7667             keyup : true
7668         });
7669 };
7670
7671 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7672      /**
7673      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7674       automatic validation (defaults to "keyup").
7675      */
7676     validationEvent : "keyup",
7677      /**
7678      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7679      */
7680     validateOnBlur : true,
7681     /**
7682      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7683      */
7684     validationDelay : 250,
7685      /**
7686      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7687      */
7688     focusClass : "x-form-focus",  // not needed???
7689     
7690        
7691     /**
7692      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7693      */
7694     invalidClass : "has-warning",
7695     
7696     /**
7697      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7698      */
7699     validClass : "has-success",
7700     
7701     /**
7702      * @cfg {Boolean} hasFeedback (true|false) default true
7703      */
7704     hasFeedback : true,
7705     
7706     /**
7707      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7708      */
7709     invalidFeedbackClass : "glyphicon-warning-sign",
7710     
7711     /**
7712      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7713      */
7714     validFeedbackClass : "glyphicon-ok",
7715     
7716     /**
7717      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7718      */
7719     selectOnFocus : false,
7720     
7721      /**
7722      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7723      */
7724     maskRe : null,
7725        /**
7726      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7727      */
7728     vtype : null,
7729     
7730       /**
7731      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7732      */
7733     disableKeyFilter : false,
7734     
7735        /**
7736      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7737      */
7738     disabled : false,
7739      /**
7740      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7741      */
7742     allowBlank : true,
7743     /**
7744      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7745      */
7746     blankText : "This field is required",
7747     
7748      /**
7749      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7750      */
7751     minLength : 0,
7752     /**
7753      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7754      */
7755     maxLength : Number.MAX_VALUE,
7756     /**
7757      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7758      */
7759     minLengthText : "The minimum length for this field is {0}",
7760     /**
7761      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7762      */
7763     maxLengthText : "The maximum length for this field is {0}",
7764   
7765     
7766     /**
7767      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7768      * If available, this function will be called only after the basic validators all return true, and will be passed the
7769      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7770      */
7771     validator : null,
7772     /**
7773      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7774      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7775      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7776      */
7777     regex : null,
7778     /**
7779      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7780      */
7781     regexText : "",
7782     
7783     autocomplete: false,
7784     
7785     
7786     fieldLabel : '',
7787     inputType : 'text',
7788     
7789     name : false,
7790     placeholder: false,
7791     before : false,
7792     after : false,
7793     size : false,
7794     hasFocus : false,
7795     preventMark: false,
7796     isFormField : true,
7797     value : '',
7798     labelWidth : 2,
7799     labelAlign : false,
7800     readOnly : false,
7801     align : false,
7802     formatedValue : false,
7803     forceFeedback : false,
7804     
7805     parentLabelAlign : function()
7806     {
7807         var parent = this;
7808         while (parent.parent()) {
7809             parent = parent.parent();
7810             if (typeof(parent.labelAlign) !='undefined') {
7811                 return parent.labelAlign;
7812             }
7813         }
7814         return 'left';
7815         
7816     },
7817     
7818     getAutoCreate : function(){
7819         
7820         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7821         
7822         var id = Roo.id();
7823         
7824         var cfg = {};
7825         
7826         if(this.inputType != 'hidden'){
7827             cfg.cls = 'form-group' //input-group
7828         }
7829         
7830         var input =  {
7831             tag: 'input',
7832             id : id,
7833             type : this.inputType,
7834             value : this.value,
7835             cls : 'form-control',
7836             placeholder : this.placeholder || '',
7837             autocomplete : this.autocomplete || 'new-password'
7838         };
7839         
7840         
7841         if(this.align){
7842             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7843         }
7844         
7845         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7846             input.maxLength = this.maxLength;
7847         }
7848         
7849         if (this.disabled) {
7850             input.disabled=true;
7851         }
7852         
7853         if (this.readOnly) {
7854             input.readonly=true;
7855         }
7856         
7857         if (this.name) {
7858             input.name = this.name;
7859         }
7860         if (this.size) {
7861             input.cls += ' input-' + this.size;
7862         }
7863         var settings=this;
7864         ['xs','sm','md','lg'].map(function(size){
7865             if (settings[size]) {
7866                 cfg.cls += ' col-' + size + '-' + settings[size];
7867             }
7868         });
7869         
7870         var inputblock = input;
7871         
7872         var feedback = {
7873             tag: 'span',
7874             cls: 'glyphicon form-control-feedback'
7875         };
7876             
7877         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7878             
7879             inputblock = {
7880                 cls : 'has-feedback',
7881                 cn :  [
7882                     input,
7883                     feedback
7884                 ] 
7885             };  
7886         }
7887         
7888         if (this.before || this.after) {
7889             
7890             inputblock = {
7891                 cls : 'input-group',
7892                 cn :  [] 
7893             };
7894             
7895             if (this.before && typeof(this.before) == 'string') {
7896                 
7897                 inputblock.cn.push({
7898                     tag :'span',
7899                     cls : 'roo-input-before input-group-addon',
7900                     html : this.before
7901                 });
7902             }
7903             if (this.before && typeof(this.before) == 'object') {
7904                 this.before = Roo.factory(this.before);
7905                 Roo.log(this.before);
7906                 inputblock.cn.push({
7907                     tag :'span',
7908                     cls : 'roo-input-before input-group-' +
7909                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7910                 });
7911             }
7912             
7913             inputblock.cn.push(input);
7914             
7915             if (this.after && typeof(this.after) == 'string') {
7916                 inputblock.cn.push({
7917                     tag :'span',
7918                     cls : 'roo-input-after input-group-addon',
7919                     html : this.after
7920                 });
7921             }
7922             if (this.after && typeof(this.after) == 'object') {
7923                 this.after = Roo.factory(this.after);
7924                 Roo.log(this.after);
7925                 inputblock.cn.push({
7926                     tag :'span',
7927                     cls : 'roo-input-after input-group-' +
7928                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7929                 });
7930             }
7931             
7932             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7933                 inputblock.cls += ' has-feedback';
7934                 inputblock.cn.push(feedback);
7935             }
7936         };
7937         
7938         if (align ==='left' && this.fieldLabel.length) {
7939                 Roo.log("left and has label");
7940                 cfg.cn = [
7941                     
7942                     {
7943                         tag: 'label',
7944                         'for' :  id,
7945                         cls : 'control-label col-sm-' + this.labelWidth,
7946                         html : this.fieldLabel
7947                         
7948                     },
7949                     {
7950                         cls : "col-sm-" + (12 - this.labelWidth), 
7951                         cn: [
7952                             inputblock
7953                         ]
7954                     }
7955                     
7956                 ];
7957         } else if ( this.fieldLabel.length) {
7958                 Roo.log(" label");
7959                  cfg.cn = [
7960                    
7961                     {
7962                         tag: 'label',
7963                         //cls : 'input-group-addon',
7964                         html : this.fieldLabel
7965                         
7966                     },
7967                     
7968                     inputblock
7969                     
7970                 ];
7971
7972         } else {
7973             
7974                 Roo.log(" no label && no align");
7975                 cfg.cn = [
7976                     
7977                         inputblock
7978                     
7979                 ];
7980                 
7981                 
7982         };
7983         Roo.log('input-parentType: ' + this.parentType);
7984         
7985         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7986            cfg.cls += ' navbar-form';
7987            Roo.log(cfg);
7988         }
7989         
7990         return cfg;
7991         
7992     },
7993     /**
7994      * return the real input element.
7995      */
7996     inputEl: function ()
7997     {
7998         return this.el.select('input.form-control',true).first();
7999     },
8000     
8001     tooltipEl : function()
8002     {
8003         return this.inputEl();
8004     },
8005     
8006     setDisabled : function(v)
8007     {
8008         var i  = this.inputEl().dom;
8009         if (!v) {
8010             i.removeAttribute('disabled');
8011             return;
8012             
8013         }
8014         i.setAttribute('disabled','true');
8015     },
8016     initEvents : function()
8017     {
8018           
8019         this.inputEl().on("keydown" , this.fireKey,  this);
8020         this.inputEl().on("focus", this.onFocus,  this);
8021         this.inputEl().on("blur", this.onBlur,  this);
8022         
8023         this.inputEl().relayEvent('keyup', this);
8024  
8025         // reference to original value for reset
8026         this.originalValue = this.getValue();
8027         //Roo.form.TextField.superclass.initEvents.call(this);
8028         if(this.validationEvent == 'keyup'){
8029             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8030             this.inputEl().on('keyup', this.filterValidation, this);
8031         }
8032         else if(this.validationEvent !== false){
8033             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8034         }
8035         
8036         if(this.selectOnFocus){
8037             this.on("focus", this.preFocus, this);
8038             
8039         }
8040         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8041             this.inputEl().on("keypress", this.filterKeys, this);
8042         }
8043        /* if(this.grow){
8044             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8045             this.el.on("click", this.autoSize,  this);
8046         }
8047         */
8048         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8049             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8050         }
8051         
8052         if (typeof(this.before) == 'object') {
8053             this.before.render(this.el.select('.roo-input-before',true).first());
8054         }
8055         if (typeof(this.after) == 'object') {
8056             this.after.render(this.el.select('.roo-input-after',true).first());
8057         }
8058         
8059         
8060     },
8061     filterValidation : function(e){
8062         if(!e.isNavKeyPress()){
8063             this.validationTask.delay(this.validationDelay);
8064         }
8065     },
8066      /**
8067      * Validates the field value
8068      * @return {Boolean} True if the value is valid, else false
8069      */
8070     validate : function(){
8071         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8072         if(this.disabled || this.validateValue(this.getRawValue())){
8073             this.markValid();
8074             return true;
8075         }
8076         
8077         this.markInvalid();
8078         return false;
8079     },
8080     
8081     
8082     /**
8083      * Validates a value according to the field's validation rules and marks the field as invalid
8084      * if the validation fails
8085      * @param {Mixed} value The value to validate
8086      * @return {Boolean} True if the value is valid, else false
8087      */
8088     validateValue : function(value){
8089         if(value.length < 1)  { // if it's blank
8090             if(this.allowBlank){
8091                 return true;
8092             }
8093             return false;
8094         }
8095         
8096         if(value.length < this.minLength){
8097             return false;
8098         }
8099         if(value.length > this.maxLength){
8100             return false;
8101         }
8102         if(this.vtype){
8103             var vt = Roo.form.VTypes;
8104             if(!vt[this.vtype](value, this)){
8105                 return false;
8106             }
8107         }
8108         if(typeof this.validator == "function"){
8109             var msg = this.validator(value);
8110             if(msg !== true){
8111                 return false;
8112             }
8113         }
8114         
8115         if(this.regex && !this.regex.test(value)){
8116             return false;
8117         }
8118         
8119         return true;
8120     },
8121
8122     
8123     
8124      // private
8125     fireKey : function(e){
8126         //Roo.log('field ' + e.getKey());
8127         if(e.isNavKeyPress()){
8128             this.fireEvent("specialkey", this, e);
8129         }
8130     },
8131     focus : function (selectText){
8132         if(this.rendered){
8133             this.inputEl().focus();
8134             if(selectText === true){
8135                 this.inputEl().dom.select();
8136             }
8137         }
8138         return this;
8139     } ,
8140     
8141     onFocus : function(){
8142         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8143            // this.el.addClass(this.focusClass);
8144         }
8145         if(!this.hasFocus){
8146             this.hasFocus = true;
8147             this.startValue = this.getValue();
8148             this.fireEvent("focus", this);
8149         }
8150     },
8151     
8152     beforeBlur : Roo.emptyFn,
8153
8154     
8155     // private
8156     onBlur : function(){
8157         this.beforeBlur();
8158         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8159             //this.el.removeClass(this.focusClass);
8160         }
8161         this.hasFocus = false;
8162         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8163             this.validate();
8164         }
8165         var v = this.getValue();
8166         if(String(v) !== String(this.startValue)){
8167             this.fireEvent('change', this, v, this.startValue);
8168         }
8169         this.fireEvent("blur", this);
8170     },
8171     
8172     /**
8173      * Resets the current field value to the originally loaded value and clears any validation messages
8174      */
8175     reset : function(){
8176         this.setValue(this.originalValue);
8177         this.validate();
8178     },
8179      /**
8180      * Returns the name of the field
8181      * @return {Mixed} name The name field
8182      */
8183     getName: function(){
8184         return this.name;
8185     },
8186      /**
8187      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8188      * @return {Mixed} value The field value
8189      */
8190     getValue : function(){
8191         
8192         var v = this.inputEl().getValue();
8193         
8194         return v;
8195     },
8196     /**
8197      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8198      * @return {Mixed} value The field value
8199      */
8200     getRawValue : function(){
8201         var v = this.inputEl().getValue();
8202         
8203         return v;
8204     },
8205     
8206     /**
8207      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8208      * @param {Mixed} value The value to set
8209      */
8210     setRawValue : function(v){
8211         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8212     },
8213     
8214     selectText : function(start, end){
8215         var v = this.getRawValue();
8216         if(v.length > 0){
8217             start = start === undefined ? 0 : start;
8218             end = end === undefined ? v.length : end;
8219             var d = this.inputEl().dom;
8220             if(d.setSelectionRange){
8221                 d.setSelectionRange(start, end);
8222             }else if(d.createTextRange){
8223                 var range = d.createTextRange();
8224                 range.moveStart("character", start);
8225                 range.moveEnd("character", v.length-end);
8226                 range.select();
8227             }
8228         }
8229     },
8230     
8231     /**
8232      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8233      * @param {Mixed} value The value to set
8234      */
8235     setValue : function(v){
8236         this.value = v;
8237         if(this.rendered){
8238             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8239             this.validate();
8240         }
8241     },
8242     
8243     /*
8244     processValue : function(value){
8245         if(this.stripCharsRe){
8246             var newValue = value.replace(this.stripCharsRe, '');
8247             if(newValue !== value){
8248                 this.setRawValue(newValue);
8249                 return newValue;
8250             }
8251         }
8252         return value;
8253     },
8254   */
8255     preFocus : function(){
8256         
8257         if(this.selectOnFocus){
8258             this.inputEl().dom.select();
8259         }
8260     },
8261     filterKeys : function(e){
8262         var k = e.getKey();
8263         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8264             return;
8265         }
8266         var c = e.getCharCode(), cc = String.fromCharCode(c);
8267         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8268             return;
8269         }
8270         if(!this.maskRe.test(cc)){
8271             e.stopEvent();
8272         }
8273     },
8274      /**
8275      * Clear any invalid styles/messages for this field
8276      */
8277     clearInvalid : function(){
8278         
8279         if(!this.el || this.preventMark){ // not rendered
8280             return;
8281         }
8282         this.el.removeClass(this.invalidClass);
8283         
8284         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8285             
8286             var feedback = this.el.select('.form-control-feedback', true).first();
8287             
8288             if(feedback){
8289                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8290             }
8291             
8292         }
8293         
8294         this.fireEvent('valid', this);
8295     },
8296     
8297      /**
8298      * Mark this field as valid
8299      */
8300     markValid : function(){
8301         if(!this.el  || this.preventMark){ // not rendered
8302             return;
8303         }
8304         
8305         this.el.removeClass([this.invalidClass, this.validClass]);
8306         
8307         var feedback = this.el.select('.form-control-feedback', true).first();
8308             
8309         if(feedback){
8310             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8311         }
8312
8313         if(this.disabled || this.allowBlank){
8314             return;
8315         }
8316         
8317         this.el.addClass(this.validClass);
8318         
8319         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8320             
8321             var feedback = this.el.select('.form-control-feedback', true).first();
8322             
8323             if(feedback){
8324                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8325                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8326             }
8327             
8328         }
8329         
8330         this.fireEvent('valid', this);
8331     },
8332     
8333      /**
8334      * Mark this field as invalid
8335      * @param {String} msg The validation message
8336      */
8337     markInvalid : function(msg)
8338     {
8339         if(!this.el  || this.preventMark){ // not rendered
8340             return;
8341         }
8342         
8343         this.el.removeClass([this.invalidClass, this.validClass]);
8344         
8345         var feedback = this.el.select('.form-control-feedback', true).first();
8346             
8347         if(feedback){
8348             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8349         }
8350
8351         if(this.disabled || this.allowBlank){
8352             return;
8353         }
8354         
8355         this.el.addClass(this.invalidClass);
8356         
8357         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8358             
8359             var feedback = this.el.select('.form-control-feedback', true).first();
8360             
8361             if(feedback){
8362                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8363                 
8364                 if(this.getValue().length || this.forceFeedback){
8365                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8366                 }
8367                 
8368             }
8369             
8370         }
8371         
8372         this.fireEvent('invalid', this, msg);
8373     },
8374     // private
8375     SafariOnKeyDown : function(event)
8376     {
8377         // this is a workaround for a password hang bug on chrome/ webkit.
8378         
8379         var isSelectAll = false;
8380         
8381         if(this.inputEl().dom.selectionEnd > 0){
8382             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8383         }
8384         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8385             event.preventDefault();
8386             this.setValue('');
8387             return;
8388         }
8389         
8390         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8391             
8392             event.preventDefault();
8393             // this is very hacky as keydown always get's upper case.
8394             //
8395             var cc = String.fromCharCode(event.getCharCode());
8396             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8397             
8398         }
8399     },
8400     adjustWidth : function(tag, w){
8401         tag = tag.toLowerCase();
8402         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8403             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8404                 if(tag == 'input'){
8405                     return w + 2;
8406                 }
8407                 if(tag == 'textarea'){
8408                     return w-2;
8409                 }
8410             }else if(Roo.isOpera){
8411                 if(tag == 'input'){
8412                     return w + 2;
8413                 }
8414                 if(tag == 'textarea'){
8415                     return w-2;
8416                 }
8417             }
8418         }
8419         return w;
8420     }
8421     
8422 });
8423
8424  
8425 /*
8426  * - LGPL
8427  *
8428  * Input
8429  * 
8430  */
8431
8432 /**
8433  * @class Roo.bootstrap.TextArea
8434  * @extends Roo.bootstrap.Input
8435  * Bootstrap TextArea class
8436  * @cfg {Number} cols Specifies the visible width of a text area
8437  * @cfg {Number} rows Specifies the visible number of lines in a text area
8438  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8439  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8440  * @cfg {string} html text
8441  * 
8442  * @constructor
8443  * Create a new TextArea
8444  * @param {Object} config The config object
8445  */
8446
8447 Roo.bootstrap.TextArea = function(config){
8448     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8449    
8450 };
8451
8452 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8453      
8454     cols : false,
8455     rows : 5,
8456     readOnly : false,
8457     warp : 'soft',
8458     resize : false,
8459     value: false,
8460     html: false,
8461     
8462     getAutoCreate : function(){
8463         
8464         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8465         
8466         var id = Roo.id();
8467         
8468         var cfg = {};
8469         
8470         var input =  {
8471             tag: 'textarea',
8472             id : id,
8473             warp : this.warp,
8474             rows : this.rows,
8475             value : this.value || '',
8476             html: this.html || '',
8477             cls : 'form-control',
8478             placeholder : this.placeholder || '' 
8479             
8480         };
8481         
8482         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8483             input.maxLength = this.maxLength;
8484         }
8485         
8486         if(this.resize){
8487             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8488         }
8489         
8490         if(this.cols){
8491             input.cols = this.cols;
8492         }
8493         
8494         if (this.readOnly) {
8495             input.readonly = true;
8496         }
8497         
8498         if (this.name) {
8499             input.name = this.name;
8500         }
8501         
8502         if (this.size) {
8503             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8504         }
8505         
8506         var settings=this;
8507         ['xs','sm','md','lg'].map(function(size){
8508             if (settings[size]) {
8509                 cfg.cls += ' col-' + size + '-' + settings[size];
8510             }
8511         });
8512         
8513         var inputblock = input;
8514         
8515         if(this.hasFeedback && !this.allowBlank){
8516             
8517             var feedback = {
8518                 tag: 'span',
8519                 cls: 'glyphicon form-control-feedback'
8520             };
8521
8522             inputblock = {
8523                 cls : 'has-feedback',
8524                 cn :  [
8525                     input,
8526                     feedback
8527                 ] 
8528             };  
8529         }
8530         
8531         
8532         if (this.before || this.after) {
8533             
8534             inputblock = {
8535                 cls : 'input-group',
8536                 cn :  [] 
8537             };
8538             if (this.before) {
8539                 inputblock.cn.push({
8540                     tag :'span',
8541                     cls : 'input-group-addon',
8542                     html : this.before
8543                 });
8544             }
8545             
8546             inputblock.cn.push(input);
8547             
8548             if(this.hasFeedback && !this.allowBlank){
8549                 inputblock.cls += ' has-feedback';
8550                 inputblock.cn.push(feedback);
8551             }
8552             
8553             if (this.after) {
8554                 inputblock.cn.push({
8555                     tag :'span',
8556                     cls : 'input-group-addon',
8557                     html : this.after
8558                 });
8559             }
8560             
8561         }
8562         
8563         if (align ==='left' && this.fieldLabel.length) {
8564                 Roo.log("left and has label");
8565                 cfg.cn = [
8566                     
8567                     {
8568                         tag: 'label',
8569                         'for' :  id,
8570                         cls : 'control-label col-sm-' + this.labelWidth,
8571                         html : this.fieldLabel
8572                         
8573                     },
8574                     {
8575                         cls : "col-sm-" + (12 - this.labelWidth), 
8576                         cn: [
8577                             inputblock
8578                         ]
8579                     }
8580                     
8581                 ];
8582         } else if ( this.fieldLabel.length) {
8583                 Roo.log(" label");
8584                  cfg.cn = [
8585                    
8586                     {
8587                         tag: 'label',
8588                         //cls : 'input-group-addon',
8589                         html : this.fieldLabel
8590                         
8591                     },
8592                     
8593                     inputblock
8594                     
8595                 ];
8596
8597         } else {
8598             
8599                    Roo.log(" no label && no align");
8600                 cfg.cn = [
8601                     
8602                         inputblock
8603                     
8604                 ];
8605                 
8606                 
8607         }
8608         
8609         if (this.disabled) {
8610             input.disabled=true;
8611         }
8612         
8613         return cfg;
8614         
8615     },
8616     /**
8617      * return the real textarea element.
8618      */
8619     inputEl: function ()
8620     {
8621         return this.el.select('textarea.form-control',true).first();
8622     }
8623 });
8624
8625  
8626 /*
8627  * - LGPL
8628  *
8629  * trigger field - base class for combo..
8630  * 
8631  */
8632  
8633 /**
8634  * @class Roo.bootstrap.TriggerField
8635  * @extends Roo.bootstrap.Input
8636  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8637  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8638  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8639  * for which you can provide a custom implementation.  For example:
8640  * <pre><code>
8641 var trigger = new Roo.bootstrap.TriggerField();
8642 trigger.onTriggerClick = myTriggerFn;
8643 trigger.applyTo('my-field');
8644 </code></pre>
8645  *
8646  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8647  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8648  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8649  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8650  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8651
8652  * @constructor
8653  * Create a new TriggerField.
8654  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8655  * to the base TextField)
8656  */
8657 Roo.bootstrap.TriggerField = function(config){
8658     this.mimicing = false;
8659     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8660 };
8661
8662 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8663     /**
8664      * @cfg {String} triggerClass A CSS class to apply to the trigger
8665      */
8666      /**
8667      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8668      */
8669     hideTrigger:false,
8670
8671     /**
8672      * @cfg {Boolean} removable (true|false) special filter default false
8673      */
8674     removable : false,
8675     
8676     /** @cfg {Boolean} grow @hide */
8677     /** @cfg {Number} growMin @hide */
8678     /** @cfg {Number} growMax @hide */
8679
8680     /**
8681      * @hide 
8682      * @method
8683      */
8684     autoSize: Roo.emptyFn,
8685     // private
8686     monitorTab : true,
8687     // private
8688     deferHeight : true,
8689
8690     
8691     actionMode : 'wrap',
8692     
8693     caret : false,
8694     
8695     
8696     getAutoCreate : function(){
8697        
8698         var align = this.labelAlign || this.parentLabelAlign();
8699         
8700         var id = Roo.id();
8701         
8702         var cfg = {
8703             cls: 'form-group' //input-group
8704         };
8705         
8706         
8707         var input =  {
8708             tag: 'input',
8709             id : id,
8710             type : this.inputType,
8711             cls : 'form-control',
8712             autocomplete: 'new-password',
8713             placeholder : this.placeholder || '' 
8714             
8715         };
8716         if (this.name) {
8717             input.name = this.name;
8718         }
8719         if (this.size) {
8720             input.cls += ' input-' + this.size;
8721         }
8722         
8723         if (this.disabled) {
8724             input.disabled=true;
8725         }
8726         
8727         var inputblock = input;
8728         
8729         if(this.hasFeedback && !this.allowBlank){
8730             
8731             var feedback = {
8732                 tag: 'span',
8733                 cls: 'glyphicon form-control-feedback'
8734             };
8735             
8736             if(this.removable && !this.editable && !this.tickable){
8737                 inputblock = {
8738                     cls : 'has-feedback',
8739                     cn :  [
8740                         inputblock,
8741                         {
8742                             tag: 'button',
8743                             html : 'x',
8744                             cls : 'roo-combo-removable-btn close'
8745                         },
8746                         feedback
8747                     ] 
8748                 };
8749             } else {
8750                 inputblock = {
8751                     cls : 'has-feedback',
8752                     cn :  [
8753                         inputblock,
8754                         feedback
8755                     ] 
8756                 };
8757             }
8758
8759         } else {
8760             if(this.removable && !this.editable && !this.tickable){
8761                 inputblock = {
8762                     cls : 'roo-removable',
8763                     cn :  [
8764                         inputblock,
8765                         {
8766                             tag: 'button',
8767                             html : 'x',
8768                             cls : 'roo-combo-removable-btn close'
8769                         }
8770                     ] 
8771                 };
8772             }
8773         }
8774         
8775         if (this.before || this.after) {
8776             
8777             inputblock = {
8778                 cls : 'input-group',
8779                 cn :  [] 
8780             };
8781             if (this.before) {
8782                 inputblock.cn.push({
8783                     tag :'span',
8784                     cls : 'input-group-addon',
8785                     html : this.before
8786                 });
8787             }
8788             
8789             inputblock.cn.push(input);
8790             
8791             if(this.hasFeedback && !this.allowBlank){
8792                 inputblock.cls += ' has-feedback';
8793                 inputblock.cn.push(feedback);
8794             }
8795             
8796             if (this.after) {
8797                 inputblock.cn.push({
8798                     tag :'span',
8799                     cls : 'input-group-addon',
8800                     html : this.after
8801                 });
8802             }
8803             
8804         };
8805         
8806         var box = {
8807             tag: 'div',
8808             cn: [
8809                 {
8810                     tag: 'input',
8811                     type : 'hidden',
8812                     cls: 'form-hidden-field'
8813                 },
8814                 inputblock
8815             ]
8816             
8817         };
8818         
8819         if(this.multiple){
8820             Roo.log('multiple');
8821             
8822             box = {
8823                 tag: 'div',
8824                 cn: [
8825                     {
8826                         tag: 'input',
8827                         type : 'hidden',
8828                         cls: 'form-hidden-field'
8829                     },
8830                     {
8831                         tag: 'ul',
8832                         cls: 'select2-choices',
8833                         cn:[
8834                             {
8835                                 tag: 'li',
8836                                 cls: 'select2-search-field',
8837                                 cn: [
8838
8839                                     inputblock
8840                                 ]
8841                             }
8842                         ]
8843                     }
8844                 ]
8845             }
8846         };
8847         
8848         var combobox = {
8849             cls: 'select2-container input-group',
8850             cn: [
8851                 box
8852 //                {
8853 //                    tag: 'ul',
8854 //                    cls: 'typeahead typeahead-long dropdown-menu',
8855 //                    style: 'display:none'
8856 //                }
8857             ]
8858         };
8859         
8860         if(!this.multiple && this.showToggleBtn){
8861             
8862             var caret = {
8863                         tag: 'span',
8864                         cls: 'caret'
8865              };
8866             if (this.caret != false) {
8867                 caret = {
8868                      tag: 'i',
8869                      cls: 'fa fa-' + this.caret
8870                 };
8871                 
8872             }
8873             
8874             combobox.cn.push({
8875                 tag :'span',
8876                 cls : 'input-group-addon btn dropdown-toggle',
8877                 cn : [
8878                     caret,
8879                     {
8880                         tag: 'span',
8881                         cls: 'combobox-clear',
8882                         cn  : [
8883                             {
8884                                 tag : 'i',
8885                                 cls: 'icon-remove'
8886                             }
8887                         ]
8888                     }
8889                 ]
8890
8891             })
8892         }
8893         
8894         if(this.multiple){
8895             combobox.cls += ' select2-container-multi';
8896         }
8897         
8898         if (align ==='left' && this.fieldLabel.length) {
8899             
8900                 Roo.log("left and has label");
8901                 cfg.cn = [
8902                     
8903                     {
8904                         tag: 'label',
8905                         'for' :  id,
8906                         cls : 'control-label col-sm-' + this.labelWidth,
8907                         html : this.fieldLabel
8908                         
8909                     },
8910                     {
8911                         cls : "col-sm-" + (12 - this.labelWidth), 
8912                         cn: [
8913                             combobox
8914                         ]
8915                     }
8916                     
8917                 ];
8918         } else if ( this.fieldLabel.length) {
8919                 Roo.log(" label");
8920                  cfg.cn = [
8921                    
8922                     {
8923                         tag: 'label',
8924                         //cls : 'input-group-addon',
8925                         html : this.fieldLabel
8926                         
8927                     },
8928                     
8929                     combobox
8930                     
8931                 ];
8932
8933         } else {
8934             
8935                 Roo.log(" no label && no align");
8936                 cfg = combobox
8937                      
8938                 
8939         }
8940          
8941         var settings=this;
8942         ['xs','sm','md','lg'].map(function(size){
8943             if (settings[size]) {
8944                 cfg.cls += ' col-' + size + '-' + settings[size];
8945             }
8946         });
8947         Roo.log(cfg);
8948         return cfg;
8949         
8950     },
8951     
8952     
8953     
8954     // private
8955     onResize : function(w, h){
8956 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8957 //        if(typeof w == 'number'){
8958 //            var x = w - this.trigger.getWidth();
8959 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8960 //            this.trigger.setStyle('left', x+'px');
8961 //        }
8962     },
8963
8964     // private
8965     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8966
8967     // private
8968     getResizeEl : function(){
8969         return this.inputEl();
8970     },
8971
8972     // private
8973     getPositionEl : function(){
8974         return this.inputEl();
8975     },
8976
8977     // private
8978     alignErrorIcon : function(){
8979         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8980     },
8981
8982     // private
8983     initEvents : function(){
8984         
8985         this.createList();
8986         
8987         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8988         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8989         if(!this.multiple && this.showToggleBtn){
8990             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8991             if(this.hideTrigger){
8992                 this.trigger.setDisplayed(false);
8993             }
8994             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8995         }
8996         
8997         if(this.multiple){
8998             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8999         }
9000         
9001         if(this.removable && !this.editable && !this.tickable){
9002             var close = this.closeTriggerEl();
9003             
9004             if(close){
9005                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9006                 close.on('click', this.removeBtnClick, this, close);
9007             }
9008         }
9009         
9010         //this.trigger.addClassOnOver('x-form-trigger-over');
9011         //this.trigger.addClassOnClick('x-form-trigger-click');
9012         
9013         //if(!this.width){
9014         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9015         //}
9016     },
9017     
9018     closeTriggerEl : function()
9019     {
9020         var close = this.el.select('.roo-combo-removable-btn', true).first();
9021         return close ? close : false;
9022     },
9023     
9024     removeBtnClick : function(e, h, el)
9025     {
9026         e.preventDefault();
9027         
9028         if(this.fireEvent("remove", this) !== false){
9029             this.reset();
9030         }
9031     },
9032     
9033     createList : function()
9034     {
9035         this.list = Roo.get(document.body).createChild({
9036             tag: 'ul',
9037             cls: 'typeahead typeahead-long dropdown-menu',
9038             style: 'display:none'
9039         });
9040         
9041         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9042         
9043     },
9044
9045     // private
9046     initTrigger : function(){
9047        
9048     },
9049
9050     // private
9051     onDestroy : function(){
9052         if(this.trigger){
9053             this.trigger.removeAllListeners();
9054           //  this.trigger.remove();
9055         }
9056         //if(this.wrap){
9057         //    this.wrap.remove();
9058         //}
9059         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9060     },
9061
9062     // private
9063     onFocus : function(){
9064         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9065         /*
9066         if(!this.mimicing){
9067             this.wrap.addClass('x-trigger-wrap-focus');
9068             this.mimicing = true;
9069             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9070             if(this.monitorTab){
9071                 this.el.on("keydown", this.checkTab, this);
9072             }
9073         }
9074         */
9075     },
9076
9077     // private
9078     checkTab : function(e){
9079         if(e.getKey() == e.TAB){
9080             this.triggerBlur();
9081         }
9082     },
9083
9084     // private
9085     onBlur : function(){
9086         // do nothing
9087     },
9088
9089     // private
9090     mimicBlur : function(e, t){
9091         /*
9092         if(!this.wrap.contains(t) && this.validateBlur()){
9093             this.triggerBlur();
9094         }
9095         */
9096     },
9097
9098     // private
9099     triggerBlur : function(){
9100         this.mimicing = false;
9101         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9102         if(this.monitorTab){
9103             this.el.un("keydown", this.checkTab, this);
9104         }
9105         //this.wrap.removeClass('x-trigger-wrap-focus');
9106         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9107     },
9108
9109     // private
9110     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9111     validateBlur : function(e, t){
9112         return true;
9113     },
9114
9115     // private
9116     onDisable : function(){
9117         this.inputEl().dom.disabled = true;
9118         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9119         //if(this.wrap){
9120         //    this.wrap.addClass('x-item-disabled');
9121         //}
9122     },
9123
9124     // private
9125     onEnable : function(){
9126         this.inputEl().dom.disabled = false;
9127         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9128         //if(this.wrap){
9129         //    this.el.removeClass('x-item-disabled');
9130         //}
9131     },
9132
9133     // private
9134     onShow : function(){
9135         var ae = this.getActionEl();
9136         
9137         if(ae){
9138             ae.dom.style.display = '';
9139             ae.dom.style.visibility = 'visible';
9140         }
9141     },
9142
9143     // private
9144     
9145     onHide : function(){
9146         var ae = this.getActionEl();
9147         ae.dom.style.display = 'none';
9148     },
9149
9150     /**
9151      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9152      * by an implementing function.
9153      * @method
9154      * @param {EventObject} e
9155      */
9156     onTriggerClick : Roo.emptyFn
9157 });
9158  /*
9159  * Based on:
9160  * Ext JS Library 1.1.1
9161  * Copyright(c) 2006-2007, Ext JS, LLC.
9162  *
9163  * Originally Released Under LGPL - original licence link has changed is not relivant.
9164  *
9165  * Fork - LGPL
9166  * <script type="text/javascript">
9167  */
9168
9169
9170 /**
9171  * @class Roo.data.SortTypes
9172  * @singleton
9173  * Defines the default sorting (casting?) comparison functions used when sorting data.
9174  */
9175 Roo.data.SortTypes = {
9176     /**
9177      * Default sort that does nothing
9178      * @param {Mixed} s The value being converted
9179      * @return {Mixed} The comparison value
9180      */
9181     none : function(s){
9182         return s;
9183     },
9184     
9185     /**
9186      * The regular expression used to strip tags
9187      * @type {RegExp}
9188      * @property
9189      */
9190     stripTagsRE : /<\/?[^>]+>/gi,
9191     
9192     /**
9193      * Strips all HTML tags to sort on text only
9194      * @param {Mixed} s The value being converted
9195      * @return {String} The comparison value
9196      */
9197     asText : function(s){
9198         return String(s).replace(this.stripTagsRE, "");
9199     },
9200     
9201     /**
9202      * Strips all HTML tags to sort on text only - Case insensitive
9203      * @param {Mixed} s The value being converted
9204      * @return {String} The comparison value
9205      */
9206     asUCText : function(s){
9207         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9208     },
9209     
9210     /**
9211      * Case insensitive string
9212      * @param {Mixed} s The value being converted
9213      * @return {String} The comparison value
9214      */
9215     asUCString : function(s) {
9216         return String(s).toUpperCase();
9217     },
9218     
9219     /**
9220      * Date sorting
9221      * @param {Mixed} s The value being converted
9222      * @return {Number} The comparison value
9223      */
9224     asDate : function(s) {
9225         if(!s){
9226             return 0;
9227         }
9228         if(s instanceof Date){
9229             return s.getTime();
9230         }
9231         return Date.parse(String(s));
9232     },
9233     
9234     /**
9235      * Float sorting
9236      * @param {Mixed} s The value being converted
9237      * @return {Float} The comparison value
9238      */
9239     asFloat : function(s) {
9240         var val = parseFloat(String(s).replace(/,/g, ""));
9241         if(isNaN(val)) val = 0;
9242         return val;
9243     },
9244     
9245     /**
9246      * Integer sorting
9247      * @param {Mixed} s The value being converted
9248      * @return {Number} The comparison value
9249      */
9250     asInt : function(s) {
9251         var val = parseInt(String(s).replace(/,/g, ""));
9252         if(isNaN(val)) val = 0;
9253         return val;
9254     }
9255 };/*
9256  * Based on:
9257  * Ext JS Library 1.1.1
9258  * Copyright(c) 2006-2007, Ext JS, LLC.
9259  *
9260  * Originally Released Under LGPL - original licence link has changed is not relivant.
9261  *
9262  * Fork - LGPL
9263  * <script type="text/javascript">
9264  */
9265
9266 /**
9267 * @class Roo.data.Record
9268  * Instances of this class encapsulate both record <em>definition</em> information, and record
9269  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9270  * to access Records cached in an {@link Roo.data.Store} object.<br>
9271  * <p>
9272  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9273  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9274  * objects.<br>
9275  * <p>
9276  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9277  * @constructor
9278  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9279  * {@link #create}. The parameters are the same.
9280  * @param {Array} data An associative Array of data values keyed by the field name.
9281  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9282  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9283  * not specified an integer id is generated.
9284  */
9285 Roo.data.Record = function(data, id){
9286     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9287     this.data = data;
9288 };
9289
9290 /**
9291  * Generate a constructor for a specific record layout.
9292  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9293  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9294  * Each field definition object may contain the following properties: <ul>
9295  * <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,
9296  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9297  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9298  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9299  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9300  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9301  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9302  * this may be omitted.</p></li>
9303  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9304  * <ul><li>auto (Default, implies no conversion)</li>
9305  * <li>string</li>
9306  * <li>int</li>
9307  * <li>float</li>
9308  * <li>boolean</li>
9309  * <li>date</li></ul></p></li>
9310  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9311  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9312  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9313  * by the Reader into an object that will be stored in the Record. It is passed the
9314  * following parameters:<ul>
9315  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9316  * </ul></p></li>
9317  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9318  * </ul>
9319  * <br>usage:<br><pre><code>
9320 var TopicRecord = Roo.data.Record.create(
9321     {name: 'title', mapping: 'topic_title'},
9322     {name: 'author', mapping: 'username'},
9323     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9324     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9325     {name: 'lastPoster', mapping: 'user2'},
9326     {name: 'excerpt', mapping: 'post_text'}
9327 );
9328
9329 var myNewRecord = new TopicRecord({
9330     title: 'Do my job please',
9331     author: 'noobie',
9332     totalPosts: 1,
9333     lastPost: new Date(),
9334     lastPoster: 'Animal',
9335     excerpt: 'No way dude!'
9336 });
9337 myStore.add(myNewRecord);
9338 </code></pre>
9339  * @method create
9340  * @static
9341  */
9342 Roo.data.Record.create = function(o){
9343     var f = function(){
9344         f.superclass.constructor.apply(this, arguments);
9345     };
9346     Roo.extend(f, Roo.data.Record);
9347     var p = f.prototype;
9348     p.fields = new Roo.util.MixedCollection(false, function(field){
9349         return field.name;
9350     });
9351     for(var i = 0, len = o.length; i < len; i++){
9352         p.fields.add(new Roo.data.Field(o[i]));
9353     }
9354     f.getField = function(name){
9355         return p.fields.get(name);  
9356     };
9357     return f;
9358 };
9359
9360 Roo.data.Record.AUTO_ID = 1000;
9361 Roo.data.Record.EDIT = 'edit';
9362 Roo.data.Record.REJECT = 'reject';
9363 Roo.data.Record.COMMIT = 'commit';
9364
9365 Roo.data.Record.prototype = {
9366     /**
9367      * Readonly flag - true if this record has been modified.
9368      * @type Boolean
9369      */
9370     dirty : false,
9371     editing : false,
9372     error: null,
9373     modified: null,
9374
9375     // private
9376     join : function(store){
9377         this.store = store;
9378     },
9379
9380     /**
9381      * Set the named field to the specified value.
9382      * @param {String} name The name of the field to set.
9383      * @param {Object} value The value to set the field to.
9384      */
9385     set : function(name, value){
9386         if(this.data[name] == value){
9387             return;
9388         }
9389         this.dirty = true;
9390         if(!this.modified){
9391             this.modified = {};
9392         }
9393         if(typeof this.modified[name] == 'undefined'){
9394             this.modified[name] = this.data[name];
9395         }
9396         this.data[name] = value;
9397         if(!this.editing && this.store){
9398             this.store.afterEdit(this);
9399         }       
9400     },
9401
9402     /**
9403      * Get the value of the named field.
9404      * @param {String} name The name of the field to get the value of.
9405      * @return {Object} The value of the field.
9406      */
9407     get : function(name){
9408         return this.data[name]; 
9409     },
9410
9411     // private
9412     beginEdit : function(){
9413         this.editing = true;
9414         this.modified = {}; 
9415     },
9416
9417     // private
9418     cancelEdit : function(){
9419         this.editing = false;
9420         delete this.modified;
9421     },
9422
9423     // private
9424     endEdit : function(){
9425         this.editing = false;
9426         if(this.dirty && this.store){
9427             this.store.afterEdit(this);
9428         }
9429     },
9430
9431     /**
9432      * Usually called by the {@link Roo.data.Store} which owns the Record.
9433      * Rejects all changes made to the Record since either creation, or the last commit operation.
9434      * Modified fields are reverted to their original values.
9435      * <p>
9436      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9437      * of reject operations.
9438      */
9439     reject : function(){
9440         var m = this.modified;
9441         for(var n in m){
9442             if(typeof m[n] != "function"){
9443                 this.data[n] = m[n];
9444             }
9445         }
9446         this.dirty = false;
9447         delete this.modified;
9448         this.editing = false;
9449         if(this.store){
9450             this.store.afterReject(this);
9451         }
9452     },
9453
9454     /**
9455      * Usually called by the {@link Roo.data.Store} which owns the Record.
9456      * Commits all changes made to the Record since either creation, or the last commit operation.
9457      * <p>
9458      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9459      * of commit operations.
9460      */
9461     commit : function(){
9462         this.dirty = false;
9463         delete this.modified;
9464         this.editing = false;
9465         if(this.store){
9466             this.store.afterCommit(this);
9467         }
9468     },
9469
9470     // private
9471     hasError : function(){
9472         return this.error != null;
9473     },
9474
9475     // private
9476     clearError : function(){
9477         this.error = null;
9478     },
9479
9480     /**
9481      * Creates a copy of this record.
9482      * @param {String} id (optional) A new record id if you don't want to use this record's id
9483      * @return {Record}
9484      */
9485     copy : function(newId) {
9486         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9487     }
9488 };/*
9489  * Based on:
9490  * Ext JS Library 1.1.1
9491  * Copyright(c) 2006-2007, Ext JS, LLC.
9492  *
9493  * Originally Released Under LGPL - original licence link has changed is not relivant.
9494  *
9495  * Fork - LGPL
9496  * <script type="text/javascript">
9497  */
9498
9499
9500
9501 /**
9502  * @class Roo.data.Store
9503  * @extends Roo.util.Observable
9504  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9505  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9506  * <p>
9507  * 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
9508  * has no knowledge of the format of the data returned by the Proxy.<br>
9509  * <p>
9510  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9511  * instances from the data object. These records are cached and made available through accessor functions.
9512  * @constructor
9513  * Creates a new Store.
9514  * @param {Object} config A config object containing the objects needed for the Store to access data,
9515  * and read the data into Records.
9516  */
9517 Roo.data.Store = function(config){
9518     this.data = new Roo.util.MixedCollection(false);
9519     this.data.getKey = function(o){
9520         return o.id;
9521     };
9522     this.baseParams = {};
9523     // private
9524     this.paramNames = {
9525         "start" : "start",
9526         "limit" : "limit",
9527         "sort" : "sort",
9528         "dir" : "dir",
9529         "multisort" : "_multisort"
9530     };
9531
9532     if(config && config.data){
9533         this.inlineData = config.data;
9534         delete config.data;
9535     }
9536
9537     Roo.apply(this, config);
9538     
9539     if(this.reader){ // reader passed
9540         this.reader = Roo.factory(this.reader, Roo.data);
9541         this.reader.xmodule = this.xmodule || false;
9542         if(!this.recordType){
9543             this.recordType = this.reader.recordType;
9544         }
9545         if(this.reader.onMetaChange){
9546             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9547         }
9548     }
9549
9550     if(this.recordType){
9551         this.fields = this.recordType.prototype.fields;
9552     }
9553     this.modified = [];
9554
9555     this.addEvents({
9556         /**
9557          * @event datachanged
9558          * Fires when the data cache has changed, and a widget which is using this Store
9559          * as a Record cache should refresh its view.
9560          * @param {Store} this
9561          */
9562         datachanged : true,
9563         /**
9564          * @event metachange
9565          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9566          * @param {Store} this
9567          * @param {Object} meta The JSON metadata
9568          */
9569         metachange : true,
9570         /**
9571          * @event add
9572          * Fires when Records have been added to the Store
9573          * @param {Store} this
9574          * @param {Roo.data.Record[]} records The array of Records added
9575          * @param {Number} index The index at which the record(s) were added
9576          */
9577         add : true,
9578         /**
9579          * @event remove
9580          * Fires when a Record has been removed from the Store
9581          * @param {Store} this
9582          * @param {Roo.data.Record} record The Record that was removed
9583          * @param {Number} index The index at which the record was removed
9584          */
9585         remove : true,
9586         /**
9587          * @event update
9588          * Fires when a Record has been updated
9589          * @param {Store} this
9590          * @param {Roo.data.Record} record The Record that was updated
9591          * @param {String} operation The update operation being performed.  Value may be one of:
9592          * <pre><code>
9593  Roo.data.Record.EDIT
9594  Roo.data.Record.REJECT
9595  Roo.data.Record.COMMIT
9596          * </code></pre>
9597          */
9598         update : true,
9599         /**
9600          * @event clear
9601          * Fires when the data cache has been cleared.
9602          * @param {Store} this
9603          */
9604         clear : true,
9605         /**
9606          * @event beforeload
9607          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9608          * the load action will be canceled.
9609          * @param {Store} this
9610          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9611          */
9612         beforeload : true,
9613         /**
9614          * @event beforeloadadd
9615          * Fires after a new set of Records has been loaded.
9616          * @param {Store} this
9617          * @param {Roo.data.Record[]} records The Records that were loaded
9618          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9619          */
9620         beforeloadadd : true,
9621         /**
9622          * @event load
9623          * Fires after a new set of Records has been loaded, before they are added to the store.
9624          * @param {Store} this
9625          * @param {Roo.data.Record[]} records The Records that were loaded
9626          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9627          * @params {Object} return from reader
9628          */
9629         load : true,
9630         /**
9631          * @event loadexception
9632          * Fires if an exception occurs in the Proxy during loading.
9633          * Called with the signature of the Proxy's "loadexception" event.
9634          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9635          * 
9636          * @param {Proxy} 
9637          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9638          * @param {Object} load options 
9639          * @param {Object} jsonData from your request (normally this contains the Exception)
9640          */
9641         loadexception : true
9642     });
9643     
9644     if(this.proxy){
9645         this.proxy = Roo.factory(this.proxy, Roo.data);
9646         this.proxy.xmodule = this.xmodule || false;
9647         this.relayEvents(this.proxy,  ["loadexception"]);
9648     }
9649     this.sortToggle = {};
9650     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9651
9652     Roo.data.Store.superclass.constructor.call(this);
9653
9654     if(this.inlineData){
9655         this.loadData(this.inlineData);
9656         delete this.inlineData;
9657     }
9658 };
9659
9660 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9661      /**
9662     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9663     * without a remote query - used by combo/forms at present.
9664     */
9665     
9666     /**
9667     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9668     */
9669     /**
9670     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9671     */
9672     /**
9673     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9674     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9675     */
9676     /**
9677     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9678     * on any HTTP request
9679     */
9680     /**
9681     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9682     */
9683     /**
9684     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9685     */
9686     multiSort: false,
9687     /**
9688     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9689     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9690     */
9691     remoteSort : false,
9692
9693     /**
9694     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9695      * loaded or when a record is removed. (defaults to false).
9696     */
9697     pruneModifiedRecords : false,
9698
9699     // private
9700     lastOptions : null,
9701
9702     /**
9703      * Add Records to the Store and fires the add event.
9704      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9705      */
9706     add : function(records){
9707         records = [].concat(records);
9708         for(var i = 0, len = records.length; i < len; i++){
9709             records[i].join(this);
9710         }
9711         var index = this.data.length;
9712         this.data.addAll(records);
9713         this.fireEvent("add", this, records, index);
9714     },
9715
9716     /**
9717      * Remove a Record from the Store and fires the remove event.
9718      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9719      */
9720     remove : function(record){
9721         var index = this.data.indexOf(record);
9722         this.data.removeAt(index);
9723         if(this.pruneModifiedRecords){
9724             this.modified.remove(record);
9725         }
9726         this.fireEvent("remove", this, record, index);
9727     },
9728
9729     /**
9730      * Remove all Records from the Store and fires the clear event.
9731      */
9732     removeAll : function(){
9733         this.data.clear();
9734         if(this.pruneModifiedRecords){
9735             this.modified = [];
9736         }
9737         this.fireEvent("clear", this);
9738     },
9739
9740     /**
9741      * Inserts Records to the Store at the given index and fires the add event.
9742      * @param {Number} index The start index at which to insert the passed Records.
9743      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9744      */
9745     insert : function(index, records){
9746         records = [].concat(records);
9747         for(var i = 0, len = records.length; i < len; i++){
9748             this.data.insert(index, records[i]);
9749             records[i].join(this);
9750         }
9751         this.fireEvent("add", this, records, index);
9752     },
9753
9754     /**
9755      * Get the index within the cache of the passed Record.
9756      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9757      * @return {Number} The index of the passed Record. Returns -1 if not found.
9758      */
9759     indexOf : function(record){
9760         return this.data.indexOf(record);
9761     },
9762
9763     /**
9764      * Get the index within the cache of the Record with the passed id.
9765      * @param {String} id The id of the Record to find.
9766      * @return {Number} The index of the Record. Returns -1 if not found.
9767      */
9768     indexOfId : function(id){
9769         return this.data.indexOfKey(id);
9770     },
9771
9772     /**
9773      * Get the Record with the specified id.
9774      * @param {String} id The id of the Record to find.
9775      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9776      */
9777     getById : function(id){
9778         return this.data.key(id);
9779     },
9780
9781     /**
9782      * Get the Record at the specified index.
9783      * @param {Number} index The index of the Record to find.
9784      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9785      */
9786     getAt : function(index){
9787         return this.data.itemAt(index);
9788     },
9789
9790     /**
9791      * Returns a range of Records between specified indices.
9792      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9793      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9794      * @return {Roo.data.Record[]} An array of Records
9795      */
9796     getRange : function(start, end){
9797         return this.data.getRange(start, end);
9798     },
9799
9800     // private
9801     storeOptions : function(o){
9802         o = Roo.apply({}, o);
9803         delete o.callback;
9804         delete o.scope;
9805         this.lastOptions = o;
9806     },
9807
9808     /**
9809      * Loads the Record cache from the configured Proxy using the configured Reader.
9810      * <p>
9811      * If using remote paging, then the first load call must specify the <em>start</em>
9812      * and <em>limit</em> properties in the options.params property to establish the initial
9813      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9814      * <p>
9815      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9816      * and this call will return before the new data has been loaded. Perform any post-processing
9817      * in a callback function, or in a "load" event handler.</strong>
9818      * <p>
9819      * @param {Object} options An object containing properties which control loading options:<ul>
9820      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9821      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9822      * passed the following arguments:<ul>
9823      * <li>r : Roo.data.Record[]</li>
9824      * <li>options: Options object from the load call</li>
9825      * <li>success: Boolean success indicator</li></ul></li>
9826      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9827      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9828      * </ul>
9829      */
9830     load : function(options){
9831         options = options || {};
9832         if(this.fireEvent("beforeload", this, options) !== false){
9833             this.storeOptions(options);
9834             var p = Roo.apply(options.params || {}, this.baseParams);
9835             // if meta was not loaded from remote source.. try requesting it.
9836             if (!this.reader.metaFromRemote) {
9837                 p._requestMeta = 1;
9838             }
9839             if(this.sortInfo && this.remoteSort){
9840                 var pn = this.paramNames;
9841                 p[pn["sort"]] = this.sortInfo.field;
9842                 p[pn["dir"]] = this.sortInfo.direction;
9843             }
9844             if (this.multiSort) {
9845                 var pn = this.paramNames;
9846                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9847             }
9848             
9849             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9850         }
9851     },
9852
9853     /**
9854      * Reloads the Record cache from the configured Proxy using the configured Reader and
9855      * the options from the last load operation performed.
9856      * @param {Object} options (optional) An object containing properties which may override the options
9857      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9858      * the most recently used options are reused).
9859      */
9860     reload : function(options){
9861         this.load(Roo.applyIf(options||{}, this.lastOptions));
9862     },
9863
9864     // private
9865     // Called as a callback by the Reader during a load operation.
9866     loadRecords : function(o, options, success){
9867         if(!o || success === false){
9868             if(success !== false){
9869                 this.fireEvent("load", this, [], options, o);
9870             }
9871             if(options.callback){
9872                 options.callback.call(options.scope || this, [], options, false);
9873             }
9874             return;
9875         }
9876         // if data returned failure - throw an exception.
9877         if (o.success === false) {
9878             // show a message if no listener is registered.
9879             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9880                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9881             }
9882             // loadmask wil be hooked into this..
9883             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9884             return;
9885         }
9886         var r = o.records, t = o.totalRecords || r.length;
9887         
9888         this.fireEvent("beforeloadadd", this, r, options, o);
9889         
9890         if(!options || options.add !== true){
9891             if(this.pruneModifiedRecords){
9892                 this.modified = [];
9893             }
9894             for(var i = 0, len = r.length; i < len; i++){
9895                 r[i].join(this);
9896             }
9897             if(this.snapshot){
9898                 this.data = this.snapshot;
9899                 delete this.snapshot;
9900             }
9901             this.data.clear();
9902             this.data.addAll(r);
9903             this.totalLength = t;
9904             this.applySort();
9905             this.fireEvent("datachanged", this);
9906         }else{
9907             this.totalLength = Math.max(t, this.data.length+r.length);
9908             this.add(r);
9909         }
9910         this.fireEvent("load", this, r, options, o);
9911         if(options.callback){
9912             options.callback.call(options.scope || this, r, options, true);
9913         }
9914     },
9915
9916
9917     /**
9918      * Loads data from a passed data block. A Reader which understands the format of the data
9919      * must have been configured in the constructor.
9920      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9921      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9922      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9923      */
9924     loadData : function(o, append){
9925         var r = this.reader.readRecords(o);
9926         this.loadRecords(r, {add: append}, true);
9927     },
9928
9929     /**
9930      * Gets the number of cached records.
9931      * <p>
9932      * <em>If using paging, this may not be the total size of the dataset. If the data object
9933      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9934      * the data set size</em>
9935      */
9936     getCount : function(){
9937         return this.data.length || 0;
9938     },
9939
9940     /**
9941      * Gets the total number of records in the dataset as returned by the server.
9942      * <p>
9943      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9944      * the dataset size</em>
9945      */
9946     getTotalCount : function(){
9947         return this.totalLength || 0;
9948     },
9949
9950     /**
9951      * Returns the sort state of the Store as an object with two properties:
9952      * <pre><code>
9953  field {String} The name of the field by which the Records are sorted
9954  direction {String} The sort order, "ASC" or "DESC"
9955      * </code></pre>
9956      */
9957     getSortState : function(){
9958         return this.sortInfo;
9959     },
9960
9961     // private
9962     applySort : function(){
9963         if(this.sortInfo && !this.remoteSort){
9964             var s = this.sortInfo, f = s.field;
9965             var st = this.fields.get(f).sortType;
9966             var fn = function(r1, r2){
9967                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9968                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9969             };
9970             this.data.sort(s.direction, fn);
9971             if(this.snapshot && this.snapshot != this.data){
9972                 this.snapshot.sort(s.direction, fn);
9973             }
9974         }
9975     },
9976
9977     /**
9978      * Sets the default sort column and order to be used by the next load operation.
9979      * @param {String} fieldName The name of the field to sort by.
9980      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9981      */
9982     setDefaultSort : function(field, dir){
9983         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9984     },
9985
9986     /**
9987      * Sort the Records.
9988      * If remote sorting is used, the sort is performed on the server, and the cache is
9989      * reloaded. If local sorting is used, the cache is sorted internally.
9990      * @param {String} fieldName The name of the field to sort by.
9991      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9992      */
9993     sort : function(fieldName, dir){
9994         var f = this.fields.get(fieldName);
9995         if(!dir){
9996             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9997             
9998             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9999                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10000             }else{
10001                 dir = f.sortDir;
10002             }
10003         }
10004         this.sortToggle[f.name] = dir;
10005         this.sortInfo = {field: f.name, direction: dir};
10006         if(!this.remoteSort){
10007             this.applySort();
10008             this.fireEvent("datachanged", this);
10009         }else{
10010             this.load(this.lastOptions);
10011         }
10012     },
10013
10014     /**
10015      * Calls the specified function for each of the Records in the cache.
10016      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10017      * Returning <em>false</em> aborts and exits the iteration.
10018      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10019      */
10020     each : function(fn, scope){
10021         this.data.each(fn, scope);
10022     },
10023
10024     /**
10025      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10026      * (e.g., during paging).
10027      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10028      */
10029     getModifiedRecords : function(){
10030         return this.modified;
10031     },
10032
10033     // private
10034     createFilterFn : function(property, value, anyMatch){
10035         if(!value.exec){ // not a regex
10036             value = String(value);
10037             if(value.length == 0){
10038                 return false;
10039             }
10040             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10041         }
10042         return function(r){
10043             return value.test(r.data[property]);
10044         };
10045     },
10046
10047     /**
10048      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10049      * @param {String} property A field on your records
10050      * @param {Number} start The record index to start at (defaults to 0)
10051      * @param {Number} end The last record index to include (defaults to length - 1)
10052      * @return {Number} The sum
10053      */
10054     sum : function(property, start, end){
10055         var rs = this.data.items, v = 0;
10056         start = start || 0;
10057         end = (end || end === 0) ? end : rs.length-1;
10058
10059         for(var i = start; i <= end; i++){
10060             v += (rs[i].data[property] || 0);
10061         }
10062         return v;
10063     },
10064
10065     /**
10066      * Filter the records by a specified property.
10067      * @param {String} field A field on your records
10068      * @param {String/RegExp} value Either a string that the field
10069      * should start with or a RegExp to test against the field
10070      * @param {Boolean} anyMatch True to match any part not just the beginning
10071      */
10072     filter : function(property, value, anyMatch){
10073         var fn = this.createFilterFn(property, value, anyMatch);
10074         return fn ? this.filterBy(fn) : this.clearFilter();
10075     },
10076
10077     /**
10078      * Filter by a function. The specified function will be called with each
10079      * record in this data source. If the function returns true the record is included,
10080      * otherwise it is filtered.
10081      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10082      * @param {Object} scope (optional) The scope of the function (defaults to this)
10083      */
10084     filterBy : function(fn, scope){
10085         this.snapshot = this.snapshot || this.data;
10086         this.data = this.queryBy(fn, scope||this);
10087         this.fireEvent("datachanged", this);
10088     },
10089
10090     /**
10091      * Query the records by a specified property.
10092      * @param {String} field A field on your records
10093      * @param {String/RegExp} value Either a string that the field
10094      * should start with or a RegExp to test against the field
10095      * @param {Boolean} anyMatch True to match any part not just the beginning
10096      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10097      */
10098     query : function(property, value, anyMatch){
10099         var fn = this.createFilterFn(property, value, anyMatch);
10100         return fn ? this.queryBy(fn) : this.data.clone();
10101     },
10102
10103     /**
10104      * Query by a function. The specified function will be called with each
10105      * record in this data source. If the function returns true the record is included
10106      * in the results.
10107      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10108      * @param {Object} scope (optional) The scope of the function (defaults to this)
10109       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10110      **/
10111     queryBy : function(fn, scope){
10112         var data = this.snapshot || this.data;
10113         return data.filterBy(fn, scope||this);
10114     },
10115
10116     /**
10117      * Collects unique values for a particular dataIndex from this store.
10118      * @param {String} dataIndex The property to collect
10119      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10120      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10121      * @return {Array} An array of the unique values
10122      **/
10123     collect : function(dataIndex, allowNull, bypassFilter){
10124         var d = (bypassFilter === true && this.snapshot) ?
10125                 this.snapshot.items : this.data.items;
10126         var v, sv, r = [], l = {};
10127         for(var i = 0, len = d.length; i < len; i++){
10128             v = d[i].data[dataIndex];
10129             sv = String(v);
10130             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10131                 l[sv] = true;
10132                 r[r.length] = v;
10133             }
10134         }
10135         return r;
10136     },
10137
10138     /**
10139      * Revert to a view of the Record cache with no filtering applied.
10140      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10141      */
10142     clearFilter : function(suppressEvent){
10143         if(this.snapshot && this.snapshot != this.data){
10144             this.data = this.snapshot;
10145             delete this.snapshot;
10146             if(suppressEvent !== true){
10147                 this.fireEvent("datachanged", this);
10148             }
10149         }
10150     },
10151
10152     // private
10153     afterEdit : function(record){
10154         if(this.modified.indexOf(record) == -1){
10155             this.modified.push(record);
10156         }
10157         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10158     },
10159     
10160     // private
10161     afterReject : function(record){
10162         this.modified.remove(record);
10163         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10164     },
10165
10166     // private
10167     afterCommit : function(record){
10168         this.modified.remove(record);
10169         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10170     },
10171
10172     /**
10173      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10174      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10175      */
10176     commitChanges : function(){
10177         var m = this.modified.slice(0);
10178         this.modified = [];
10179         for(var i = 0, len = m.length; i < len; i++){
10180             m[i].commit();
10181         }
10182     },
10183
10184     /**
10185      * Cancel outstanding changes on all changed records.
10186      */
10187     rejectChanges : function(){
10188         var m = this.modified.slice(0);
10189         this.modified = [];
10190         for(var i = 0, len = m.length; i < len; i++){
10191             m[i].reject();
10192         }
10193     },
10194
10195     onMetaChange : function(meta, rtype, o){
10196         this.recordType = rtype;
10197         this.fields = rtype.prototype.fields;
10198         delete this.snapshot;
10199         this.sortInfo = meta.sortInfo || this.sortInfo;
10200         this.modified = [];
10201         this.fireEvent('metachange', this, this.reader.meta);
10202     },
10203     
10204     moveIndex : function(data, type)
10205     {
10206         var index = this.indexOf(data);
10207         
10208         var newIndex = index + type;
10209         
10210         this.remove(data);
10211         
10212         this.insert(newIndex, data);
10213         
10214     }
10215 });/*
10216  * Based on:
10217  * Ext JS Library 1.1.1
10218  * Copyright(c) 2006-2007, Ext JS, LLC.
10219  *
10220  * Originally Released Under LGPL - original licence link has changed is not relivant.
10221  *
10222  * Fork - LGPL
10223  * <script type="text/javascript">
10224  */
10225
10226 /**
10227  * @class Roo.data.SimpleStore
10228  * @extends Roo.data.Store
10229  * Small helper class to make creating Stores from Array data easier.
10230  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10231  * @cfg {Array} fields An array of field definition objects, or field name strings.
10232  * @cfg {Array} data The multi-dimensional array of data
10233  * @constructor
10234  * @param {Object} config
10235  */
10236 Roo.data.SimpleStore = function(config){
10237     Roo.data.SimpleStore.superclass.constructor.call(this, {
10238         isLocal : true,
10239         reader: new Roo.data.ArrayReader({
10240                 id: config.id
10241             },
10242             Roo.data.Record.create(config.fields)
10243         ),
10244         proxy : new Roo.data.MemoryProxy(config.data)
10245     });
10246     this.load();
10247 };
10248 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10249  * Based on:
10250  * Ext JS Library 1.1.1
10251  * Copyright(c) 2006-2007, Ext JS, LLC.
10252  *
10253  * Originally Released Under LGPL - original licence link has changed is not relivant.
10254  *
10255  * Fork - LGPL
10256  * <script type="text/javascript">
10257  */
10258
10259 /**
10260 /**
10261  * @extends Roo.data.Store
10262  * @class Roo.data.JsonStore
10263  * Small helper class to make creating Stores for JSON data easier. <br/>
10264 <pre><code>
10265 var store = new Roo.data.JsonStore({
10266     url: 'get-images.php',
10267     root: 'images',
10268     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10269 });
10270 </code></pre>
10271  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10272  * JsonReader and HttpProxy (unless inline data is provided).</b>
10273  * @cfg {Array} fields An array of field definition objects, or field name strings.
10274  * @constructor
10275  * @param {Object} config
10276  */
10277 Roo.data.JsonStore = function(c){
10278     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10279         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10280         reader: new Roo.data.JsonReader(c, c.fields)
10281     }));
10282 };
10283 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10284  * Based on:
10285  * Ext JS Library 1.1.1
10286  * Copyright(c) 2006-2007, Ext JS, LLC.
10287  *
10288  * Originally Released Under LGPL - original licence link has changed is not relivant.
10289  *
10290  * Fork - LGPL
10291  * <script type="text/javascript">
10292  */
10293
10294  
10295 Roo.data.Field = function(config){
10296     if(typeof config == "string"){
10297         config = {name: config};
10298     }
10299     Roo.apply(this, config);
10300     
10301     if(!this.type){
10302         this.type = "auto";
10303     }
10304     
10305     var st = Roo.data.SortTypes;
10306     // named sortTypes are supported, here we look them up
10307     if(typeof this.sortType == "string"){
10308         this.sortType = st[this.sortType];
10309     }
10310     
10311     // set default sortType for strings and dates
10312     if(!this.sortType){
10313         switch(this.type){
10314             case "string":
10315                 this.sortType = st.asUCString;
10316                 break;
10317             case "date":
10318                 this.sortType = st.asDate;
10319                 break;
10320             default:
10321                 this.sortType = st.none;
10322         }
10323     }
10324
10325     // define once
10326     var stripRe = /[\$,%]/g;
10327
10328     // prebuilt conversion function for this field, instead of
10329     // switching every time we're reading a value
10330     if(!this.convert){
10331         var cv, dateFormat = this.dateFormat;
10332         switch(this.type){
10333             case "":
10334             case "auto":
10335             case undefined:
10336                 cv = function(v){ return v; };
10337                 break;
10338             case "string":
10339                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10340                 break;
10341             case "int":
10342                 cv = function(v){
10343                     return v !== undefined && v !== null && v !== '' ?
10344                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10345                     };
10346                 break;
10347             case "float":
10348                 cv = function(v){
10349                     return v !== undefined && v !== null && v !== '' ?
10350                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10351                     };
10352                 break;
10353             case "bool":
10354             case "boolean":
10355                 cv = function(v){ return v === true || v === "true" || v == 1; };
10356                 break;
10357             case "date":
10358                 cv = function(v){
10359                     if(!v){
10360                         return '';
10361                     }
10362                     if(v instanceof Date){
10363                         return v;
10364                     }
10365                     if(dateFormat){
10366                         if(dateFormat == "timestamp"){
10367                             return new Date(v*1000);
10368                         }
10369                         return Date.parseDate(v, dateFormat);
10370                     }
10371                     var parsed = Date.parse(v);
10372                     return parsed ? new Date(parsed) : null;
10373                 };
10374              break;
10375             
10376         }
10377         this.convert = cv;
10378     }
10379 };
10380
10381 Roo.data.Field.prototype = {
10382     dateFormat: null,
10383     defaultValue: "",
10384     mapping: null,
10385     sortType : null,
10386     sortDir : "ASC"
10387 };/*
10388  * Based on:
10389  * Ext JS Library 1.1.1
10390  * Copyright(c) 2006-2007, Ext JS, LLC.
10391  *
10392  * Originally Released Under LGPL - original licence link has changed is not relivant.
10393  *
10394  * Fork - LGPL
10395  * <script type="text/javascript">
10396  */
10397  
10398 // Base class for reading structured data from a data source.  This class is intended to be
10399 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10400
10401 /**
10402  * @class Roo.data.DataReader
10403  * Base class for reading structured data from a data source.  This class is intended to be
10404  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10405  */
10406
10407 Roo.data.DataReader = function(meta, recordType){
10408     
10409     this.meta = meta;
10410     
10411     this.recordType = recordType instanceof Array ? 
10412         Roo.data.Record.create(recordType) : recordType;
10413 };
10414
10415 Roo.data.DataReader.prototype = {
10416      /**
10417      * Create an empty record
10418      * @param {Object} data (optional) - overlay some values
10419      * @return {Roo.data.Record} record created.
10420      */
10421     newRow :  function(d) {
10422         var da =  {};
10423         this.recordType.prototype.fields.each(function(c) {
10424             switch( c.type) {
10425                 case 'int' : da[c.name] = 0; break;
10426                 case 'date' : da[c.name] = new Date(); break;
10427                 case 'float' : da[c.name] = 0.0; break;
10428                 case 'boolean' : da[c.name] = false; break;
10429                 default : da[c.name] = ""; break;
10430             }
10431             
10432         });
10433         return new this.recordType(Roo.apply(da, d));
10434     }
10435     
10436 };/*
10437  * Based on:
10438  * Ext JS Library 1.1.1
10439  * Copyright(c) 2006-2007, Ext JS, LLC.
10440  *
10441  * Originally Released Under LGPL - original licence link has changed is not relivant.
10442  *
10443  * Fork - LGPL
10444  * <script type="text/javascript">
10445  */
10446
10447 /**
10448  * @class Roo.data.DataProxy
10449  * @extends Roo.data.Observable
10450  * This class is an abstract base class for implementations which provide retrieval of
10451  * unformatted data objects.<br>
10452  * <p>
10453  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10454  * (of the appropriate type which knows how to parse the data object) to provide a block of
10455  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10456  * <p>
10457  * Custom implementations must implement the load method as described in
10458  * {@link Roo.data.HttpProxy#load}.
10459  */
10460 Roo.data.DataProxy = function(){
10461     this.addEvents({
10462         /**
10463          * @event beforeload
10464          * Fires before a network request is made to retrieve a data object.
10465          * @param {Object} This DataProxy object.
10466          * @param {Object} params The params parameter to the load function.
10467          */
10468         beforeload : true,
10469         /**
10470          * @event load
10471          * Fires before the load method's callback is called.
10472          * @param {Object} This DataProxy object.
10473          * @param {Object} o The data object.
10474          * @param {Object} arg The callback argument object passed to the load function.
10475          */
10476         load : true,
10477         /**
10478          * @event loadexception
10479          * Fires if an Exception occurs during data retrieval.
10480          * @param {Object} This DataProxy object.
10481          * @param {Object} o The data object.
10482          * @param {Object} arg The callback argument object passed to the load function.
10483          * @param {Object} e The Exception.
10484          */
10485         loadexception : true
10486     });
10487     Roo.data.DataProxy.superclass.constructor.call(this);
10488 };
10489
10490 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10491
10492     /**
10493      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10494      */
10495 /*
10496  * Based on:
10497  * Ext JS Library 1.1.1
10498  * Copyright(c) 2006-2007, Ext JS, LLC.
10499  *
10500  * Originally Released Under LGPL - original licence link has changed is not relivant.
10501  *
10502  * Fork - LGPL
10503  * <script type="text/javascript">
10504  */
10505 /**
10506  * @class Roo.data.MemoryProxy
10507  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10508  * to the Reader when its load method is called.
10509  * @constructor
10510  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10511  */
10512 Roo.data.MemoryProxy = function(data){
10513     if (data.data) {
10514         data = data.data;
10515     }
10516     Roo.data.MemoryProxy.superclass.constructor.call(this);
10517     this.data = data;
10518 };
10519
10520 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10521     /**
10522      * Load data from the requested source (in this case an in-memory
10523      * data object passed to the constructor), read the data object into
10524      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10525      * process that block using the passed callback.
10526      * @param {Object} params This parameter is not used by the MemoryProxy class.
10527      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10528      * object into a block of Roo.data.Records.
10529      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10530      * The function must be passed <ul>
10531      * <li>The Record block object</li>
10532      * <li>The "arg" argument from the load function</li>
10533      * <li>A boolean success indicator</li>
10534      * </ul>
10535      * @param {Object} scope The scope in which to call the callback
10536      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10537      */
10538     load : function(params, reader, callback, scope, arg){
10539         params = params || {};
10540         var result;
10541         try {
10542             result = reader.readRecords(this.data);
10543         }catch(e){
10544             this.fireEvent("loadexception", this, arg, null, e);
10545             callback.call(scope, null, arg, false);
10546             return;
10547         }
10548         callback.call(scope, result, arg, true);
10549     },
10550     
10551     // private
10552     update : function(params, records){
10553         
10554     }
10555 });/*
10556  * Based on:
10557  * Ext JS Library 1.1.1
10558  * Copyright(c) 2006-2007, Ext JS, LLC.
10559  *
10560  * Originally Released Under LGPL - original licence link has changed is not relivant.
10561  *
10562  * Fork - LGPL
10563  * <script type="text/javascript">
10564  */
10565 /**
10566  * @class Roo.data.HttpProxy
10567  * @extends Roo.data.DataProxy
10568  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10569  * configured to reference a certain URL.<br><br>
10570  * <p>
10571  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10572  * from which the running page was served.<br><br>
10573  * <p>
10574  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10575  * <p>
10576  * Be aware that to enable the browser to parse an XML document, the server must set
10577  * the Content-Type header in the HTTP response to "text/xml".
10578  * @constructor
10579  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10580  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10581  * will be used to make the request.
10582  */
10583 Roo.data.HttpProxy = function(conn){
10584     Roo.data.HttpProxy.superclass.constructor.call(this);
10585     // is conn a conn config or a real conn?
10586     this.conn = conn;
10587     this.useAjax = !conn || !conn.events;
10588   
10589 };
10590
10591 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10592     // thse are take from connection...
10593     
10594     /**
10595      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10596      */
10597     /**
10598      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10599      * extra parameters to each request made by this object. (defaults to undefined)
10600      */
10601     /**
10602      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10603      *  to each request made by this object. (defaults to undefined)
10604      */
10605     /**
10606      * @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)
10607      */
10608     /**
10609      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10610      */
10611      /**
10612      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10613      * @type Boolean
10614      */
10615   
10616
10617     /**
10618      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10619      * @type Boolean
10620      */
10621     /**
10622      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10623      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10624      * a finer-grained basis than the DataProxy events.
10625      */
10626     getConnection : function(){
10627         return this.useAjax ? Roo.Ajax : this.conn;
10628     },
10629
10630     /**
10631      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10632      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10633      * process that block using the passed callback.
10634      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10635      * for the request to the remote server.
10636      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10637      * object into a block of Roo.data.Records.
10638      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10639      * The function must be passed <ul>
10640      * <li>The Record block object</li>
10641      * <li>The "arg" argument from the load function</li>
10642      * <li>A boolean success indicator</li>
10643      * </ul>
10644      * @param {Object} scope The scope in which to call the callback
10645      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10646      */
10647     load : function(params, reader, callback, scope, arg){
10648         if(this.fireEvent("beforeload", this, params) !== false){
10649             var  o = {
10650                 params : params || {},
10651                 request: {
10652                     callback : callback,
10653                     scope : scope,
10654                     arg : arg
10655                 },
10656                 reader: reader,
10657                 callback : this.loadResponse,
10658                 scope: this
10659             };
10660             if(this.useAjax){
10661                 Roo.applyIf(o, this.conn);
10662                 if(this.activeRequest){
10663                     Roo.Ajax.abort(this.activeRequest);
10664                 }
10665                 this.activeRequest = Roo.Ajax.request(o);
10666             }else{
10667                 this.conn.request(o);
10668             }
10669         }else{
10670             callback.call(scope||this, null, arg, false);
10671         }
10672     },
10673
10674     // private
10675     loadResponse : function(o, success, response){
10676         delete this.activeRequest;
10677         if(!success){
10678             this.fireEvent("loadexception", this, o, response);
10679             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10680             return;
10681         }
10682         var result;
10683         try {
10684             result = o.reader.read(response);
10685         }catch(e){
10686             this.fireEvent("loadexception", this, o, response, e);
10687             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10688             return;
10689         }
10690         
10691         this.fireEvent("load", this, o, o.request.arg);
10692         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10693     },
10694
10695     // private
10696     update : function(dataSet){
10697
10698     },
10699
10700     // private
10701     updateResponse : function(dataSet){
10702
10703     }
10704 });/*
10705  * Based on:
10706  * Ext JS Library 1.1.1
10707  * Copyright(c) 2006-2007, Ext JS, LLC.
10708  *
10709  * Originally Released Under LGPL - original licence link has changed is not relivant.
10710  *
10711  * Fork - LGPL
10712  * <script type="text/javascript">
10713  */
10714
10715 /**
10716  * @class Roo.data.ScriptTagProxy
10717  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10718  * other than the originating domain of the running page.<br><br>
10719  * <p>
10720  * <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
10721  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10722  * <p>
10723  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10724  * source code that is used as the source inside a &lt;script> tag.<br><br>
10725  * <p>
10726  * In order for the browser to process the returned data, the server must wrap the data object
10727  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10728  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10729  * depending on whether the callback name was passed:
10730  * <p>
10731  * <pre><code>
10732 boolean scriptTag = false;
10733 String cb = request.getParameter("callback");
10734 if (cb != null) {
10735     scriptTag = true;
10736     response.setContentType("text/javascript");
10737 } else {
10738     response.setContentType("application/x-json");
10739 }
10740 Writer out = response.getWriter();
10741 if (scriptTag) {
10742     out.write(cb + "(");
10743 }
10744 out.print(dataBlock.toJsonString());
10745 if (scriptTag) {
10746     out.write(");");
10747 }
10748 </pre></code>
10749  *
10750  * @constructor
10751  * @param {Object} config A configuration object.
10752  */
10753 Roo.data.ScriptTagProxy = function(config){
10754     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10755     Roo.apply(this, config);
10756     this.head = document.getElementsByTagName("head")[0];
10757 };
10758
10759 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10760
10761 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10762     /**
10763      * @cfg {String} url The URL from which to request the data object.
10764      */
10765     /**
10766      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10767      */
10768     timeout : 30000,
10769     /**
10770      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10771      * the server the name of the callback function set up by the load call to process the returned data object.
10772      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10773      * javascript output which calls this named function passing the data object as its only parameter.
10774      */
10775     callbackParam : "callback",
10776     /**
10777      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10778      * name to the request.
10779      */
10780     nocache : true,
10781
10782     /**
10783      * Load data from the configured URL, read the data object into
10784      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10785      * process that block using the passed callback.
10786      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10787      * for the request to the remote server.
10788      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10789      * object into a block of Roo.data.Records.
10790      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10791      * The function must be passed <ul>
10792      * <li>The Record block object</li>
10793      * <li>The "arg" argument from the load function</li>
10794      * <li>A boolean success indicator</li>
10795      * </ul>
10796      * @param {Object} scope The scope in which to call the callback
10797      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10798      */
10799     load : function(params, reader, callback, scope, arg){
10800         if(this.fireEvent("beforeload", this, params) !== false){
10801
10802             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10803
10804             var url = this.url;
10805             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10806             if(this.nocache){
10807                 url += "&_dc=" + (new Date().getTime());
10808             }
10809             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10810             var trans = {
10811                 id : transId,
10812                 cb : "stcCallback"+transId,
10813                 scriptId : "stcScript"+transId,
10814                 params : params,
10815                 arg : arg,
10816                 url : url,
10817                 callback : callback,
10818                 scope : scope,
10819                 reader : reader
10820             };
10821             var conn = this;
10822
10823             window[trans.cb] = function(o){
10824                 conn.handleResponse(o, trans);
10825             };
10826
10827             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10828
10829             if(this.autoAbort !== false){
10830                 this.abort();
10831             }
10832
10833             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10834
10835             var script = document.createElement("script");
10836             script.setAttribute("src", url);
10837             script.setAttribute("type", "text/javascript");
10838             script.setAttribute("id", trans.scriptId);
10839             this.head.appendChild(script);
10840
10841             this.trans = trans;
10842         }else{
10843             callback.call(scope||this, null, arg, false);
10844         }
10845     },
10846
10847     // private
10848     isLoading : function(){
10849         return this.trans ? true : false;
10850     },
10851
10852     /**
10853      * Abort the current server request.
10854      */
10855     abort : function(){
10856         if(this.isLoading()){
10857             this.destroyTrans(this.trans);
10858         }
10859     },
10860
10861     // private
10862     destroyTrans : function(trans, isLoaded){
10863         this.head.removeChild(document.getElementById(trans.scriptId));
10864         clearTimeout(trans.timeoutId);
10865         if(isLoaded){
10866             window[trans.cb] = undefined;
10867             try{
10868                 delete window[trans.cb];
10869             }catch(e){}
10870         }else{
10871             // if hasn't been loaded, wait for load to remove it to prevent script error
10872             window[trans.cb] = function(){
10873                 window[trans.cb] = undefined;
10874                 try{
10875                     delete window[trans.cb];
10876                 }catch(e){}
10877             };
10878         }
10879     },
10880
10881     // private
10882     handleResponse : function(o, trans){
10883         this.trans = false;
10884         this.destroyTrans(trans, true);
10885         var result;
10886         try {
10887             result = trans.reader.readRecords(o);
10888         }catch(e){
10889             this.fireEvent("loadexception", this, o, trans.arg, e);
10890             trans.callback.call(trans.scope||window, null, trans.arg, false);
10891             return;
10892         }
10893         this.fireEvent("load", this, o, trans.arg);
10894         trans.callback.call(trans.scope||window, result, trans.arg, true);
10895     },
10896
10897     // private
10898     handleFailure : function(trans){
10899         this.trans = false;
10900         this.destroyTrans(trans, false);
10901         this.fireEvent("loadexception", this, null, trans.arg);
10902         trans.callback.call(trans.scope||window, null, trans.arg, false);
10903     }
10904 });/*
10905  * Based on:
10906  * Ext JS Library 1.1.1
10907  * Copyright(c) 2006-2007, Ext JS, LLC.
10908  *
10909  * Originally Released Under LGPL - original licence link has changed is not relivant.
10910  *
10911  * Fork - LGPL
10912  * <script type="text/javascript">
10913  */
10914
10915 /**
10916  * @class Roo.data.JsonReader
10917  * @extends Roo.data.DataReader
10918  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10919  * based on mappings in a provided Roo.data.Record constructor.
10920  * 
10921  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10922  * in the reply previously. 
10923  * 
10924  * <p>
10925  * Example code:
10926  * <pre><code>
10927 var RecordDef = Roo.data.Record.create([
10928     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10929     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10930 ]);
10931 var myReader = new Roo.data.JsonReader({
10932     totalProperty: "results",    // The property which contains the total dataset size (optional)
10933     root: "rows",                // The property which contains an Array of row objects
10934     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10935 }, RecordDef);
10936 </code></pre>
10937  * <p>
10938  * This would consume a JSON file like this:
10939  * <pre><code>
10940 { 'results': 2, 'rows': [
10941     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10942     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10943 }
10944 </code></pre>
10945  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10946  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10947  * paged from the remote server.
10948  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10949  * @cfg {String} root name of the property which contains the Array of row objects.
10950  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10951  * @cfg {Array} fields Array of field definition objects
10952  * @constructor
10953  * Create a new JsonReader
10954  * @param {Object} meta Metadata configuration options
10955  * @param {Object} recordType Either an Array of field definition objects,
10956  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10957  */
10958 Roo.data.JsonReader = function(meta, recordType){
10959     
10960     meta = meta || {};
10961     // set some defaults:
10962     Roo.applyIf(meta, {
10963         totalProperty: 'total',
10964         successProperty : 'success',
10965         root : 'data',
10966         id : 'id'
10967     });
10968     
10969     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10970 };
10971 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10972     
10973     /**
10974      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10975      * Used by Store query builder to append _requestMeta to params.
10976      * 
10977      */
10978     metaFromRemote : false,
10979     /**
10980      * This method is only used by a DataProxy which has retrieved data from a remote server.
10981      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10982      * @return {Object} data A data block which is used by an Roo.data.Store object as
10983      * a cache of Roo.data.Records.
10984      */
10985     read : function(response){
10986         var json = response.responseText;
10987        
10988         var o = /* eval:var:o */ eval("("+json+")");
10989         if(!o) {
10990             throw {message: "JsonReader.read: Json object not found"};
10991         }
10992         
10993         if(o.metaData){
10994             
10995             delete this.ef;
10996             this.metaFromRemote = true;
10997             this.meta = o.metaData;
10998             this.recordType = Roo.data.Record.create(o.metaData.fields);
10999             this.onMetaChange(this.meta, this.recordType, o);
11000         }
11001         return this.readRecords(o);
11002     },
11003
11004     // private function a store will implement
11005     onMetaChange : function(meta, recordType, o){
11006
11007     },
11008
11009     /**
11010          * @ignore
11011          */
11012     simpleAccess: function(obj, subsc) {
11013         return obj[subsc];
11014     },
11015
11016         /**
11017          * @ignore
11018          */
11019     getJsonAccessor: function(){
11020         var re = /[\[\.]/;
11021         return function(expr) {
11022             try {
11023                 return(re.test(expr))
11024                     ? new Function("obj", "return obj." + expr)
11025                     : function(obj){
11026                         return obj[expr];
11027                     };
11028             } catch(e){}
11029             return Roo.emptyFn;
11030         };
11031     }(),
11032
11033     /**
11034      * Create a data block containing Roo.data.Records from an XML document.
11035      * @param {Object} o An object which contains an Array of row objects in the property specified
11036      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11037      * which contains the total size of the dataset.
11038      * @return {Object} data A data block which is used by an Roo.data.Store object as
11039      * a cache of Roo.data.Records.
11040      */
11041     readRecords : function(o){
11042         /**
11043          * After any data loads, the raw JSON data is available for further custom processing.
11044          * @type Object
11045          */
11046         this.o = o;
11047         var s = this.meta, Record = this.recordType,
11048             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11049
11050 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11051         if (!this.ef) {
11052             if(s.totalProperty) {
11053                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11054                 }
11055                 if(s.successProperty) {
11056                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11057                 }
11058                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11059                 if (s.id) {
11060                         var g = this.getJsonAccessor(s.id);
11061                         this.getId = function(rec) {
11062                                 var r = g(rec);  
11063                                 return (r === undefined || r === "") ? null : r;
11064                         };
11065                 } else {
11066                         this.getId = function(){return null;};
11067                 }
11068             this.ef = [];
11069             for(var jj = 0; jj < fl; jj++){
11070                 f = fi[jj];
11071                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11072                 this.ef[jj] = this.getJsonAccessor(map);
11073             }
11074         }
11075
11076         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11077         if(s.totalProperty){
11078             var vt = parseInt(this.getTotal(o), 10);
11079             if(!isNaN(vt)){
11080                 totalRecords = vt;
11081             }
11082         }
11083         if(s.successProperty){
11084             var vs = this.getSuccess(o);
11085             if(vs === false || vs === 'false'){
11086                 success = false;
11087             }
11088         }
11089         var records = [];
11090         for(var i = 0; i < c; i++){
11091                 var n = root[i];
11092             var values = {};
11093             var id = this.getId(n);
11094             for(var j = 0; j < fl; j++){
11095                 f = fi[j];
11096             var v = this.ef[j](n);
11097             if (!f.convert) {
11098                 Roo.log('missing convert for ' + f.name);
11099                 Roo.log(f);
11100                 continue;
11101             }
11102             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11103             }
11104             var record = new Record(values, id);
11105             record.json = n;
11106             records[i] = record;
11107         }
11108         return {
11109             raw : o,
11110             success : success,
11111             records : records,
11112             totalRecords : totalRecords
11113         };
11114     }
11115 });/*
11116  * Based on:
11117  * Ext JS Library 1.1.1
11118  * Copyright(c) 2006-2007, Ext JS, LLC.
11119  *
11120  * Originally Released Under LGPL - original licence link has changed is not relivant.
11121  *
11122  * Fork - LGPL
11123  * <script type="text/javascript">
11124  */
11125
11126 /**
11127  * @class Roo.data.ArrayReader
11128  * @extends Roo.data.DataReader
11129  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11130  * Each element of that Array represents a row of data fields. The
11131  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11132  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11133  * <p>
11134  * Example code:.
11135  * <pre><code>
11136 var RecordDef = Roo.data.Record.create([
11137     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11138     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11139 ]);
11140 var myReader = new Roo.data.ArrayReader({
11141     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11142 }, RecordDef);
11143 </code></pre>
11144  * <p>
11145  * This would consume an Array like this:
11146  * <pre><code>
11147 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11148   </code></pre>
11149  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11150  * @constructor
11151  * Create a new JsonReader
11152  * @param {Object} meta Metadata configuration options.
11153  * @param {Object} recordType Either an Array of field definition objects
11154  * as specified to {@link Roo.data.Record#create},
11155  * or an {@link Roo.data.Record} object
11156  * created using {@link Roo.data.Record#create}.
11157  */
11158 Roo.data.ArrayReader = function(meta, recordType){
11159     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11160 };
11161
11162 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11163     /**
11164      * Create a data block containing Roo.data.Records from an XML document.
11165      * @param {Object} o An Array of row objects which represents the dataset.
11166      * @return {Object} data A data block which is used by an Roo.data.Store object as
11167      * a cache of Roo.data.Records.
11168      */
11169     readRecords : function(o){
11170         var sid = this.meta ? this.meta.id : null;
11171         var recordType = this.recordType, fields = recordType.prototype.fields;
11172         var records = [];
11173         var root = o;
11174             for(var i = 0; i < root.length; i++){
11175                     var n = root[i];
11176                 var values = {};
11177                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11178                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11179                 var f = fields.items[j];
11180                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11181                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11182                 v = f.convert(v);
11183                 values[f.name] = v;
11184             }
11185                 var record = new recordType(values, id);
11186                 record.json = n;
11187                 records[records.length] = record;
11188             }
11189             return {
11190                 records : records,
11191                 totalRecords : records.length
11192             };
11193     }
11194 });/*
11195  * - LGPL
11196  * * 
11197  */
11198
11199 /**
11200  * @class Roo.bootstrap.ComboBox
11201  * @extends Roo.bootstrap.TriggerField
11202  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11203  * @cfg {Boolean} append (true|false) default false
11204  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11205  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11206  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11207  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11208  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11209  * @cfg {Boolean} animate default true
11210  * @cfg {Boolean} emptyResultText only for touch device
11211  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11212  * @constructor
11213  * Create a new ComboBox.
11214  * @param {Object} config Configuration options
11215  */
11216 Roo.bootstrap.ComboBox = function(config){
11217     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11218     this.addEvents({
11219         /**
11220          * @event expand
11221          * Fires when the dropdown list is expanded
11222              * @param {Roo.bootstrap.ComboBox} combo This combo box
11223              */
11224         'expand' : true,
11225         /**
11226          * @event collapse
11227          * Fires when the dropdown list is collapsed
11228              * @param {Roo.bootstrap.ComboBox} combo This combo box
11229              */
11230         'collapse' : true,
11231         /**
11232          * @event beforeselect
11233          * Fires before a list item is selected. Return false to cancel the selection.
11234              * @param {Roo.bootstrap.ComboBox} combo This combo box
11235              * @param {Roo.data.Record} record The data record returned from the underlying store
11236              * @param {Number} index The index of the selected item in the dropdown list
11237              */
11238         'beforeselect' : true,
11239         /**
11240          * @event select
11241          * Fires when a list item is selected
11242              * @param {Roo.bootstrap.ComboBox} combo This combo box
11243              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11244              * @param {Number} index The index of the selected item in the dropdown list
11245              */
11246         'select' : true,
11247         /**
11248          * @event beforequery
11249          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11250          * The event object passed has these properties:
11251              * @param {Roo.bootstrap.ComboBox} combo This combo box
11252              * @param {String} query The query
11253              * @param {Boolean} forceAll true to force "all" query
11254              * @param {Boolean} cancel true to cancel the query
11255              * @param {Object} e The query event object
11256              */
11257         'beforequery': true,
11258          /**
11259          * @event add
11260          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11261              * @param {Roo.bootstrap.ComboBox} combo This combo box
11262              */
11263         'add' : true,
11264         /**
11265          * @event edit
11266          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11267              * @param {Roo.bootstrap.ComboBox} combo This combo box
11268              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11269              */
11270         'edit' : true,
11271         /**
11272          * @event remove
11273          * Fires when the remove value from the combobox array
11274              * @param {Roo.bootstrap.ComboBox} combo This combo box
11275              */
11276         'remove' : true,
11277         /**
11278          * @event specialfilter
11279          * Fires when specialfilter
11280             * @param {Roo.bootstrap.ComboBox} combo This combo box
11281             */
11282         'specialfilter' : true
11283         
11284     });
11285     
11286     this.item = [];
11287     this.tickItems = [];
11288     
11289     this.selectedIndex = -1;
11290     if(this.mode == 'local'){
11291         if(config.queryDelay === undefined){
11292             this.queryDelay = 10;
11293         }
11294         if(config.minChars === undefined){
11295             this.minChars = 0;
11296         }
11297     }
11298 };
11299
11300 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11301      
11302     /**
11303      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11304      * rendering into an Roo.Editor, defaults to false)
11305      */
11306     /**
11307      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11308      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11309      */
11310     /**
11311      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11312      */
11313     /**
11314      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11315      * the dropdown list (defaults to undefined, with no header element)
11316      */
11317
11318      /**
11319      * @cfg {String/Roo.Template} tpl The template to use to render the output
11320      */
11321      
11322      /**
11323      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11324      */
11325     listWidth: undefined,
11326     /**
11327      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11328      * mode = 'remote' or 'text' if mode = 'local')
11329      */
11330     displayField: undefined,
11331     
11332     /**
11333      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11334      * mode = 'remote' or 'value' if mode = 'local'). 
11335      * Note: use of a valueField requires the user make a selection
11336      * in order for a value to be mapped.
11337      */
11338     valueField: undefined,
11339     
11340     
11341     /**
11342      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11343      * field's data value (defaults to the underlying DOM element's name)
11344      */
11345     hiddenName: undefined,
11346     /**
11347      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11348      */
11349     listClass: '',
11350     /**
11351      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11352      */
11353     selectedClass: 'active',
11354     
11355     /**
11356      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11357      */
11358     shadow:'sides',
11359     /**
11360      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11361      * anchor positions (defaults to 'tl-bl')
11362      */
11363     listAlign: 'tl-bl?',
11364     /**
11365      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11366      */
11367     maxHeight: 300,
11368     /**
11369      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11370      * query specified by the allQuery config option (defaults to 'query')
11371      */
11372     triggerAction: 'query',
11373     /**
11374      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11375      * (defaults to 4, does not apply if editable = false)
11376      */
11377     minChars : 4,
11378     /**
11379      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11380      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11381      */
11382     typeAhead: false,
11383     /**
11384      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11385      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11386      */
11387     queryDelay: 500,
11388     /**
11389      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11390      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11391      */
11392     pageSize: 0,
11393     /**
11394      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11395      * when editable = true (defaults to false)
11396      */
11397     selectOnFocus:false,
11398     /**
11399      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11400      */
11401     queryParam: 'query',
11402     /**
11403      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11404      * when mode = 'remote' (defaults to 'Loading...')
11405      */
11406     loadingText: 'Loading...',
11407     /**
11408      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11409      */
11410     resizable: false,
11411     /**
11412      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11413      */
11414     handleHeight : 8,
11415     /**
11416      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11417      * traditional select (defaults to true)
11418      */
11419     editable: true,
11420     /**
11421      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11422      */
11423     allQuery: '',
11424     /**
11425      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11426      */
11427     mode: 'remote',
11428     /**
11429      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11430      * listWidth has a higher value)
11431      */
11432     minListWidth : 70,
11433     /**
11434      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11435      * allow the user to set arbitrary text into the field (defaults to false)
11436      */
11437     forceSelection:false,
11438     /**
11439      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11440      * if typeAhead = true (defaults to 250)
11441      */
11442     typeAheadDelay : 250,
11443     /**
11444      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11445      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11446      */
11447     valueNotFoundText : undefined,
11448     /**
11449      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11450      */
11451     blockFocus : false,
11452     
11453     /**
11454      * @cfg {Boolean} disableClear Disable showing of clear button.
11455      */
11456     disableClear : false,
11457     /**
11458      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11459      */
11460     alwaysQuery : false,
11461     
11462     /**
11463      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11464      */
11465     multiple : false,
11466     
11467     /**
11468      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11469      */
11470     invalidClass : "has-warning",
11471     
11472     /**
11473      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11474      */
11475     validClass : "has-success",
11476     
11477     /**
11478      * @cfg {Boolean} specialFilter (true|false) special filter default false
11479      */
11480     specialFilter : false,
11481     
11482     /**
11483      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11484      */
11485     mobileTouchView : true,
11486     
11487     //private
11488     addicon : false,
11489     editicon: false,
11490     
11491     page: 0,
11492     hasQuery: false,
11493     append: false,
11494     loadNext: false,
11495     autoFocus : true,
11496     tickable : false,
11497     btnPosition : 'right',
11498     triggerList : true,
11499     showToggleBtn : true,
11500     animate : true,
11501     emptyResultText: 'Empty',
11502     triggerText : 'Select',
11503     
11504     // element that contains real text value.. (when hidden is used..)
11505     
11506     getAutoCreate : function()
11507     {
11508         var cfg = false;
11509         
11510         /*
11511          * Touch Devices
11512          */
11513         
11514         if(Roo.isTouch && this.mobileTouchView){
11515             cfg = this.getAutoCreateTouchView();
11516             return cfg;;
11517         }
11518         
11519         /*
11520          *  Normal ComboBox
11521          */
11522         if(!this.tickable){
11523             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11524             return cfg;
11525         }
11526         
11527         /*
11528          *  ComboBox with tickable selections
11529          */
11530              
11531         var align = this.labelAlign || this.parentLabelAlign();
11532         
11533         cfg = {
11534             cls : 'form-group roo-combobox-tickable' //input-group
11535         };
11536         
11537         var buttons = {
11538             tag : 'div',
11539             cls : 'tickable-buttons',
11540             cn : [
11541                 {
11542                     tag : 'button',
11543                     type : 'button',
11544                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11545                     html : this.triggerText
11546                 },
11547                 {
11548                     tag : 'button',
11549                     type : 'button',
11550                     name : 'ok',
11551                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11552                     html : 'Done'
11553                 },
11554                 {
11555                     tag : 'button',
11556                     type : 'button',
11557                     name : 'cancel',
11558                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11559                     html : 'Cancel'
11560                 }
11561             ]
11562         };
11563         
11564         if(this.editable){
11565             buttons.cn.unshift({
11566                 tag: 'input',
11567                 cls: 'select2-search-field-input'
11568             });
11569         }
11570         
11571         var _this = this;
11572         
11573         Roo.each(buttons.cn, function(c){
11574             if (_this.size) {
11575                 c.cls += ' btn-' + _this.size;
11576             }
11577
11578             if (_this.disabled) {
11579                 c.disabled = true;
11580             }
11581         });
11582         
11583         var box = {
11584             tag: 'div',
11585             cn: [
11586                 {
11587                     tag: 'input',
11588                     type : 'hidden',
11589                     cls: 'form-hidden-field'
11590                 },
11591                 {
11592                     tag: 'ul',
11593                     cls: 'select2-choices',
11594                     cn:[
11595                         {
11596                             tag: 'li',
11597                             cls: 'select2-search-field',
11598                             cn: [
11599
11600                                 buttons
11601                             ]
11602                         }
11603                     ]
11604                 }
11605             ]
11606         }
11607         
11608         var combobox = {
11609             cls: 'select2-container input-group select2-container-multi',
11610             cn: [
11611                 box
11612 //                {
11613 //                    tag: 'ul',
11614 //                    cls: 'typeahead typeahead-long dropdown-menu',
11615 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11616 //                }
11617             ]
11618         };
11619         
11620         if(this.hasFeedback && !this.allowBlank){
11621             
11622             var feedback = {
11623                 tag: 'span',
11624                 cls: 'glyphicon form-control-feedback'
11625             };
11626
11627             combobox.cn.push(feedback);
11628         }
11629         
11630         if (align ==='left' && this.fieldLabel.length) {
11631             
11632                 Roo.log("left and has label");
11633                 cfg.cn = [
11634                     
11635                     {
11636                         tag: 'label',
11637                         'for' :  id,
11638                         cls : 'control-label col-sm-' + this.labelWidth,
11639                         html : this.fieldLabel
11640                         
11641                     },
11642                     {
11643                         cls : "col-sm-" + (12 - this.labelWidth), 
11644                         cn: [
11645                             combobox
11646                         ]
11647                     }
11648                     
11649                 ];
11650         } else if ( this.fieldLabel.length) {
11651                 Roo.log(" label");
11652                  cfg.cn = [
11653                    
11654                     {
11655                         tag: 'label',
11656                         //cls : 'input-group-addon',
11657                         html : this.fieldLabel
11658                         
11659                     },
11660                     
11661                     combobox
11662                     
11663                 ];
11664
11665         } else {
11666             
11667                 Roo.log(" no label && no align");
11668                 cfg = combobox
11669                      
11670                 
11671         }
11672          
11673         var settings=this;
11674         ['xs','sm','md','lg'].map(function(size){
11675             if (settings[size]) {
11676                 cfg.cls += ' col-' + size + '-' + settings[size];
11677             }
11678         });
11679         
11680         return cfg;
11681         
11682     },
11683     
11684     _initEventsCalled : false,
11685     
11686     // private
11687     initEvents: function()
11688     {
11689         
11690         if (this._initEventsCalled) { // as we call render... prevent looping...
11691             return;
11692         }
11693         this._initEventsCalled = true;
11694         
11695         if (!this.store) {
11696             throw "can not find store for combo";
11697         }
11698         
11699         this.store = Roo.factory(this.store, Roo.data);
11700         
11701         // if we are building from html. then this element is so complex, that we can not really
11702         // use the rendered HTML.
11703         // so we have to trash and replace the previous code.
11704         if (Roo.XComponent.build_from_html) {
11705             
11706             // remove this element....
11707             var e = this.el.dom, k=0;
11708             while (e ) { e = e.previousSibling;  ++k;}
11709
11710             this.el.remove();
11711             
11712             this.el=false;
11713             this.rendered = false;
11714             
11715             this.render(this.parent().getChildContainer(true), k);
11716             
11717             
11718             
11719         }
11720         
11721         
11722         /*
11723          * Touch Devices
11724          */
11725         
11726         if(Roo.isTouch && this.mobileTouchView){
11727             this.initTouchView();
11728             return;
11729         }
11730         
11731         if(this.tickable){
11732             this.initTickableEvents();
11733             return;
11734         }
11735         
11736         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11737         
11738         if(this.hiddenName){
11739             
11740             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11741             
11742             this.hiddenField.dom.value =
11743                 this.hiddenValue !== undefined ? this.hiddenValue :
11744                 this.value !== undefined ? this.value : '';
11745
11746             // prevent input submission
11747             this.el.dom.removeAttribute('name');
11748             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11749              
11750              
11751         }
11752         //if(Roo.isGecko){
11753         //    this.el.dom.setAttribute('autocomplete', 'off');
11754         //}
11755         
11756         var cls = 'x-combo-list';
11757         
11758         //this.list = new Roo.Layer({
11759         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11760         //});
11761         
11762         var _this = this;
11763         
11764         (function(){
11765             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11766             _this.list.setWidth(lw);
11767         }).defer(100);
11768         
11769         this.list.on('mouseover', this.onViewOver, this);
11770         this.list.on('mousemove', this.onViewMove, this);
11771         
11772         this.list.on('scroll', this.onViewScroll, this);
11773         
11774         /*
11775         this.list.swallowEvent('mousewheel');
11776         this.assetHeight = 0;
11777
11778         if(this.title){
11779             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11780             this.assetHeight += this.header.getHeight();
11781         }
11782
11783         this.innerList = this.list.createChild({cls:cls+'-inner'});
11784         this.innerList.on('mouseover', this.onViewOver, this);
11785         this.innerList.on('mousemove', this.onViewMove, this);
11786         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11787         
11788         if(this.allowBlank && !this.pageSize && !this.disableClear){
11789             this.footer = this.list.createChild({cls:cls+'-ft'});
11790             this.pageTb = new Roo.Toolbar(this.footer);
11791            
11792         }
11793         if(this.pageSize){
11794             this.footer = this.list.createChild({cls:cls+'-ft'});
11795             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11796                     {pageSize: this.pageSize});
11797             
11798         }
11799         
11800         if (this.pageTb && this.allowBlank && !this.disableClear) {
11801             var _this = this;
11802             this.pageTb.add(new Roo.Toolbar.Fill(), {
11803                 cls: 'x-btn-icon x-btn-clear',
11804                 text: '&#160;',
11805                 handler: function()
11806                 {
11807                     _this.collapse();
11808                     _this.clearValue();
11809                     _this.onSelect(false, -1);
11810                 }
11811             });
11812         }
11813         if (this.footer) {
11814             this.assetHeight += this.footer.getHeight();
11815         }
11816         */
11817             
11818         if(!this.tpl){
11819             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11820         }
11821
11822         this.view = new Roo.View(this.list, this.tpl, {
11823             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11824         });
11825         //this.view.wrapEl.setDisplayed(false);
11826         this.view.on('click', this.onViewClick, this);
11827         
11828         
11829         
11830         this.store.on('beforeload', this.onBeforeLoad, this);
11831         this.store.on('load', this.onLoad, this);
11832         this.store.on('loadexception', this.onLoadException, this);
11833         /*
11834         if(this.resizable){
11835             this.resizer = new Roo.Resizable(this.list,  {
11836                pinned:true, handles:'se'
11837             });
11838             this.resizer.on('resize', function(r, w, h){
11839                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11840                 this.listWidth = w;
11841                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11842                 this.restrictHeight();
11843             }, this);
11844             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11845         }
11846         */
11847         if(!this.editable){
11848             this.editable = true;
11849             this.setEditable(false);
11850         }
11851         
11852         /*
11853         
11854         if (typeof(this.events.add.listeners) != 'undefined') {
11855             
11856             this.addicon = this.wrap.createChild(
11857                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11858        
11859             this.addicon.on('click', function(e) {
11860                 this.fireEvent('add', this);
11861             }, this);
11862         }
11863         if (typeof(this.events.edit.listeners) != 'undefined') {
11864             
11865             this.editicon = this.wrap.createChild(
11866                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11867             if (this.addicon) {
11868                 this.editicon.setStyle('margin-left', '40px');
11869             }
11870             this.editicon.on('click', function(e) {
11871                 
11872                 // we fire even  if inothing is selected..
11873                 this.fireEvent('edit', this, this.lastData );
11874                 
11875             }, this);
11876         }
11877         */
11878         
11879         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11880             "up" : function(e){
11881                 this.inKeyMode = true;
11882                 this.selectPrev();
11883             },
11884
11885             "down" : function(e){
11886                 if(!this.isExpanded()){
11887                     this.onTriggerClick();
11888                 }else{
11889                     this.inKeyMode = true;
11890                     this.selectNext();
11891                 }
11892             },
11893
11894             "enter" : function(e){
11895 //                this.onViewClick();
11896                 //return true;
11897                 this.collapse();
11898                 
11899                 if(this.fireEvent("specialkey", this, e)){
11900                     this.onViewClick(false);
11901                 }
11902                 
11903                 return true;
11904             },
11905
11906             "esc" : function(e){
11907                 this.collapse();
11908             },
11909
11910             "tab" : function(e){
11911                 this.collapse();
11912                 
11913                 if(this.fireEvent("specialkey", this, e)){
11914                     this.onViewClick(false);
11915                 }
11916                 
11917                 return true;
11918             },
11919
11920             scope : this,
11921
11922             doRelay : function(foo, bar, hname){
11923                 if(hname == 'down' || this.scope.isExpanded()){
11924                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11925                 }
11926                 return true;
11927             },
11928
11929             forceKeyDown: true
11930         });
11931         
11932         
11933         this.queryDelay = Math.max(this.queryDelay || 10,
11934                 this.mode == 'local' ? 10 : 250);
11935         
11936         
11937         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11938         
11939         if(this.typeAhead){
11940             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11941         }
11942         if(this.editable !== false){
11943             this.inputEl().on("keyup", this.onKeyUp, this);
11944         }
11945         if(this.forceSelection){
11946             this.inputEl().on('blur', this.doForce, this);
11947         }
11948         
11949         if(this.multiple){
11950             this.choices = this.el.select('ul.select2-choices', true).first();
11951             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11952         }
11953     },
11954     
11955     initTickableEvents: function()
11956     {   
11957         this.createList();
11958         
11959         if(this.hiddenName){
11960             
11961             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11962             
11963             this.hiddenField.dom.value =
11964                 this.hiddenValue !== undefined ? this.hiddenValue :
11965                 this.value !== undefined ? this.value : '';
11966
11967             // prevent input submission
11968             this.el.dom.removeAttribute('name');
11969             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11970              
11971              
11972         }
11973         
11974 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11975         
11976         this.choices = this.el.select('ul.select2-choices', true).first();
11977         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11978         if(this.triggerList){
11979             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11980         }
11981          
11982         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11983         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11984         
11985         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11986         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11987         
11988         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11989         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11990         
11991         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11992         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11993         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11994         
11995         this.okBtn.hide();
11996         this.cancelBtn.hide();
11997         
11998         var _this = this;
11999         
12000         (function(){
12001             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12002             _this.list.setWidth(lw);
12003         }).defer(100);
12004         
12005         this.list.on('mouseover', this.onViewOver, this);
12006         this.list.on('mousemove', this.onViewMove, this);
12007         
12008         this.list.on('scroll', this.onViewScroll, this);
12009         
12010         if(!this.tpl){
12011             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>';
12012         }
12013
12014         this.view = new Roo.View(this.list, this.tpl, {
12015             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12016         });
12017         
12018         //this.view.wrapEl.setDisplayed(false);
12019         this.view.on('click', this.onViewClick, this);
12020         
12021         
12022         
12023         this.store.on('beforeload', this.onBeforeLoad, this);
12024         this.store.on('load', this.onLoad, this);
12025         this.store.on('loadexception', this.onLoadException, this);
12026         
12027         if(this.editable){
12028             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12029                 "up" : function(e){
12030                     this.inKeyMode = true;
12031                     this.selectPrev();
12032                 },
12033
12034                 "down" : function(e){
12035                     this.inKeyMode = true;
12036                     this.selectNext();
12037                 },
12038
12039                 "enter" : function(e){
12040                     if(this.fireEvent("specialkey", this, e)){
12041                         this.onViewClick(false);
12042                     }
12043                     
12044                     return true;
12045                 },
12046
12047                 "esc" : function(e){
12048                     this.onTickableFooterButtonClick(e, false, false);
12049                 },
12050
12051                 "tab" : function(e){
12052                     this.fireEvent("specialkey", this, e);
12053                     
12054                     this.onTickableFooterButtonClick(e, false, false);
12055                     
12056                     return true;
12057                 },
12058
12059                 scope : this,
12060
12061                 doRelay : function(e, fn, key){
12062                     if(this.scope.isExpanded()){
12063                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12064                     }
12065                     return true;
12066                 },
12067
12068                 forceKeyDown: true
12069             });
12070         }
12071         
12072         this.queryDelay = Math.max(this.queryDelay || 10,
12073                 this.mode == 'local' ? 10 : 250);
12074         
12075         
12076         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12077         
12078         if(this.typeAhead){
12079             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12080         }
12081         
12082         if(this.editable !== false){
12083             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12084         }
12085         
12086     },
12087
12088     onDestroy : function(){
12089         if(this.view){
12090             this.view.setStore(null);
12091             this.view.el.removeAllListeners();
12092             this.view.el.remove();
12093             this.view.purgeListeners();
12094         }
12095         if(this.list){
12096             this.list.dom.innerHTML  = '';
12097         }
12098         
12099         if(this.store){
12100             this.store.un('beforeload', this.onBeforeLoad, this);
12101             this.store.un('load', this.onLoad, this);
12102             this.store.un('loadexception', this.onLoadException, this);
12103         }
12104         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12105     },
12106
12107     // private
12108     fireKey : function(e){
12109         if(e.isNavKeyPress() && !this.list.isVisible()){
12110             this.fireEvent("specialkey", this, e);
12111         }
12112     },
12113
12114     // private
12115     onResize: function(w, h){
12116 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12117 //        
12118 //        if(typeof w != 'number'){
12119 //            // we do not handle it!?!?
12120 //            return;
12121 //        }
12122 //        var tw = this.trigger.getWidth();
12123 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12124 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12125 //        var x = w - tw;
12126 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12127 //            
12128 //        //this.trigger.setStyle('left', x+'px');
12129 //        
12130 //        if(this.list && this.listWidth === undefined){
12131 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12132 //            this.list.setWidth(lw);
12133 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12134 //        }
12135         
12136     
12137         
12138     },
12139
12140     /**
12141      * Allow or prevent the user from directly editing the field text.  If false is passed,
12142      * the user will only be able to select from the items defined in the dropdown list.  This method
12143      * is the runtime equivalent of setting the 'editable' config option at config time.
12144      * @param {Boolean} value True to allow the user to directly edit the field text
12145      */
12146     setEditable : function(value){
12147         if(value == this.editable){
12148             return;
12149         }
12150         this.editable = value;
12151         if(!value){
12152             this.inputEl().dom.setAttribute('readOnly', true);
12153             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12154             this.inputEl().addClass('x-combo-noedit');
12155         }else{
12156             this.inputEl().dom.setAttribute('readOnly', false);
12157             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12158             this.inputEl().removeClass('x-combo-noedit');
12159         }
12160     },
12161
12162     // private
12163     
12164     onBeforeLoad : function(combo,opts){
12165         if(!this.hasFocus){
12166             return;
12167         }
12168          if (!opts.add) {
12169             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12170          }
12171         this.restrictHeight();
12172         this.selectedIndex = -1;
12173     },
12174
12175     // private
12176     onLoad : function(){
12177         
12178         this.hasQuery = false;
12179         
12180         if(!this.hasFocus){
12181             return;
12182         }
12183         
12184         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12185             this.loading.hide();
12186         }
12187              
12188         if(this.store.getCount() > 0){
12189             this.expand();
12190             this.restrictHeight();
12191             if(this.lastQuery == this.allQuery){
12192                 if(this.editable && !this.tickable){
12193                     this.inputEl().dom.select();
12194                 }
12195                 
12196                 if(
12197                     !this.selectByValue(this.value, true) &&
12198                     this.autoFocus && 
12199                     (
12200                         !this.store.lastOptions ||
12201                         typeof(this.store.lastOptions.add) == 'undefined' || 
12202                         this.store.lastOptions.add != true
12203                     )
12204                 ){
12205                     this.select(0, true);
12206                 }
12207             }else{
12208                 if(this.autoFocus){
12209                     this.selectNext();
12210                 }
12211                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12212                     this.taTask.delay(this.typeAheadDelay);
12213                 }
12214             }
12215         }else{
12216             this.onEmptyResults();
12217         }
12218         
12219         //this.el.focus();
12220     },
12221     // private
12222     onLoadException : function()
12223     {
12224         this.hasQuery = false;
12225         
12226         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12227             this.loading.hide();
12228         }
12229         
12230         if(this.tickable && this.editable){
12231             return;
12232         }
12233         
12234         this.collapse();
12235         
12236         Roo.log(this.store.reader.jsonData);
12237         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12238             // fixme
12239             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12240         }
12241         
12242         
12243     },
12244     // private
12245     onTypeAhead : function(){
12246         if(this.store.getCount() > 0){
12247             var r = this.store.getAt(0);
12248             var newValue = r.data[this.displayField];
12249             var len = newValue.length;
12250             var selStart = this.getRawValue().length;
12251             
12252             if(selStart != len){
12253                 this.setRawValue(newValue);
12254                 this.selectText(selStart, newValue.length);
12255             }
12256         }
12257     },
12258
12259     // private
12260     onSelect : function(record, index){
12261         
12262         if(this.fireEvent('beforeselect', this, record, index) !== false){
12263         
12264             this.setFromData(index > -1 ? record.data : false);
12265             
12266             this.collapse();
12267             this.fireEvent('select', this, record, index);
12268         }
12269     },
12270
12271     /**
12272      * Returns the currently selected field value or empty string if no value is set.
12273      * @return {String} value The selected value
12274      */
12275     getValue : function(){
12276         
12277         if(this.multiple){
12278             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12279         }
12280         
12281         if(this.valueField){
12282             return typeof this.value != 'undefined' ? this.value : '';
12283         }else{
12284             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12285         }
12286     },
12287
12288     /**
12289      * Clears any text/value currently set in the field
12290      */
12291     clearValue : function(){
12292         if(this.hiddenField){
12293             this.hiddenField.dom.value = '';
12294         }
12295         this.value = '';
12296         this.setRawValue('');
12297         this.lastSelectionText = '';
12298         this.lastData = false;
12299         
12300         var close = this.closeTriggerEl();
12301         
12302         if(close){
12303             close.hide();
12304         }
12305         
12306     },
12307
12308     /**
12309      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12310      * will be displayed in the field.  If the value does not match the data value of an existing item,
12311      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12312      * Otherwise the field will be blank (although the value will still be set).
12313      * @param {String} value The value to match
12314      */
12315     setValue : function(v){
12316         if(this.multiple){
12317             this.syncValue();
12318             return;
12319         }
12320         
12321         var text = v;
12322         if(this.valueField){
12323             var r = this.findRecord(this.valueField, v);
12324             if(r){
12325                 text = r.data[this.displayField];
12326             }else if(this.valueNotFoundText !== undefined){
12327                 text = this.valueNotFoundText;
12328             }
12329         }
12330         this.lastSelectionText = text;
12331         if(this.hiddenField){
12332             this.hiddenField.dom.value = v;
12333         }
12334         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12335         this.value = v;
12336         
12337         var close = this.closeTriggerEl();
12338         
12339         if(close){
12340             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12341         }
12342     },
12343     /**
12344      * @property {Object} the last set data for the element
12345      */
12346     
12347     lastData : false,
12348     /**
12349      * Sets the value of the field based on a object which is related to the record format for the store.
12350      * @param {Object} value the value to set as. or false on reset?
12351      */
12352     setFromData : function(o){
12353         
12354         if(this.multiple){
12355             this.addItem(o);
12356             return;
12357         }
12358             
12359         var dv = ''; // display value
12360         var vv = ''; // value value..
12361         this.lastData = o;
12362         if (this.displayField) {
12363             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12364         } else {
12365             // this is an error condition!!!
12366             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12367         }
12368         
12369         if(this.valueField){
12370             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12371         }
12372         
12373         var close = this.closeTriggerEl();
12374         
12375         if(close){
12376             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12377         }
12378         
12379         if(this.hiddenField){
12380             this.hiddenField.dom.value = vv;
12381             
12382             this.lastSelectionText = dv;
12383             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12384             this.value = vv;
12385             return;
12386         }
12387         // no hidden field.. - we store the value in 'value', but still display
12388         // display field!!!!
12389         this.lastSelectionText = dv;
12390         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12391         this.value = vv;
12392         
12393         
12394         
12395     },
12396     // private
12397     reset : function(){
12398         // overridden so that last data is reset..
12399         
12400         if(this.multiple){
12401             this.clearItem();
12402             return;
12403         }
12404         
12405         this.setValue(this.originalValue);
12406         this.clearInvalid();
12407         this.lastData = false;
12408         if (this.view) {
12409             this.view.clearSelections();
12410         }
12411     },
12412     // private
12413     findRecord : function(prop, value){
12414         var record;
12415         if(this.store.getCount() > 0){
12416             this.store.each(function(r){
12417                 if(r.data[prop] == value){
12418                     record = r;
12419                     return false;
12420                 }
12421                 return true;
12422             });
12423         }
12424         return record;
12425     },
12426     
12427     getName: function()
12428     {
12429         // returns hidden if it's set..
12430         if (!this.rendered) {return ''};
12431         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12432         
12433     },
12434     // private
12435     onViewMove : function(e, t){
12436         this.inKeyMode = false;
12437     },
12438
12439     // private
12440     onViewOver : function(e, t){
12441         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12442             return;
12443         }
12444         var item = this.view.findItemFromChild(t);
12445         
12446         if(item){
12447             var index = this.view.indexOf(item);
12448             this.select(index, false);
12449         }
12450     },
12451
12452     // private
12453     onViewClick : function(view, doFocus, el, e)
12454     {
12455         var index = this.view.getSelectedIndexes()[0];
12456         
12457         var r = this.store.getAt(index);
12458         
12459         if(this.tickable){
12460             
12461             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12462                 return;
12463             }
12464             
12465             var rm = false;
12466             var _this = this;
12467             
12468             Roo.each(this.tickItems, function(v,k){
12469                 
12470                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12471                     _this.tickItems.splice(k, 1);
12472                     
12473                     if(typeof(e) == 'undefined' && view == false){
12474                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12475                     }
12476                     
12477                     rm = true;
12478                     return;
12479                 }
12480             });
12481             
12482             if(rm){
12483                 return;
12484             }
12485             
12486             this.tickItems.push(r.data);
12487             
12488             if(typeof(e) == 'undefined' && view == false){
12489                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12490             }
12491                     
12492             return;
12493         }
12494         
12495         if(r){
12496             this.onSelect(r, index);
12497         }
12498         if(doFocus !== false && !this.blockFocus){
12499             this.inputEl().focus();
12500         }
12501     },
12502
12503     // private
12504     restrictHeight : function(){
12505         //this.innerList.dom.style.height = '';
12506         //var inner = this.innerList.dom;
12507         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12508         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12509         //this.list.beginUpdate();
12510         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12511         this.list.alignTo(this.inputEl(), this.listAlign);
12512         this.list.alignTo(this.inputEl(), this.listAlign);
12513         //this.list.endUpdate();
12514     },
12515
12516     // private
12517     onEmptyResults : function(){
12518         
12519         if(this.tickable && this.editable){
12520             this.restrictHeight();
12521             return;
12522         }
12523         
12524         this.collapse();
12525     },
12526
12527     /**
12528      * Returns true if the dropdown list is expanded, else false.
12529      */
12530     isExpanded : function(){
12531         return this.list.isVisible();
12532     },
12533
12534     /**
12535      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12536      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12537      * @param {String} value The data value of the item to select
12538      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12539      * selected item if it is not currently in view (defaults to true)
12540      * @return {Boolean} True if the value matched an item in the list, else false
12541      */
12542     selectByValue : function(v, scrollIntoView){
12543         if(v !== undefined && v !== null){
12544             var r = this.findRecord(this.valueField || this.displayField, v);
12545             if(r){
12546                 this.select(this.store.indexOf(r), scrollIntoView);
12547                 return true;
12548             }
12549         }
12550         return false;
12551     },
12552
12553     /**
12554      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12555      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12556      * @param {Number} index The zero-based index of the list item to select
12557      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12558      * selected item if it is not currently in view (defaults to true)
12559      */
12560     select : function(index, scrollIntoView){
12561         this.selectedIndex = index;
12562         this.view.select(index);
12563         if(scrollIntoView !== false){
12564             var el = this.view.getNode(index);
12565             /*
12566              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12567              */
12568             if(el){
12569                 this.list.scrollChildIntoView(el, false);
12570             }
12571         }
12572     },
12573
12574     // private
12575     selectNext : function(){
12576         var ct = this.store.getCount();
12577         if(ct > 0){
12578             if(this.selectedIndex == -1){
12579                 this.select(0);
12580             }else if(this.selectedIndex < ct-1){
12581                 this.select(this.selectedIndex+1);
12582             }
12583         }
12584     },
12585
12586     // private
12587     selectPrev : function(){
12588         var ct = this.store.getCount();
12589         if(ct > 0){
12590             if(this.selectedIndex == -1){
12591                 this.select(0);
12592             }else if(this.selectedIndex != 0){
12593                 this.select(this.selectedIndex-1);
12594             }
12595         }
12596     },
12597
12598     // private
12599     onKeyUp : function(e){
12600         if(this.editable !== false && !e.isSpecialKey()){
12601             this.lastKey = e.getKey();
12602             this.dqTask.delay(this.queryDelay);
12603         }
12604     },
12605
12606     // private
12607     validateBlur : function(){
12608         return !this.list || !this.list.isVisible();   
12609     },
12610
12611     // private
12612     initQuery : function(){
12613         
12614         var v = this.getRawValue();
12615         
12616         if(this.tickable && this.editable){
12617             v = this.tickableInputEl().getValue();
12618         }
12619         
12620         this.doQuery(v);
12621     },
12622
12623     // private
12624     doForce : function(){
12625         if(this.inputEl().dom.value.length > 0){
12626             this.inputEl().dom.value =
12627                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12628              
12629         }
12630     },
12631
12632     /**
12633      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12634      * query allowing the query action to be canceled if needed.
12635      * @param {String} query The SQL query to execute
12636      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12637      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12638      * saved in the current store (defaults to false)
12639      */
12640     doQuery : function(q, forceAll){
12641         
12642         if(q === undefined || q === null){
12643             q = '';
12644         }
12645         var qe = {
12646             query: q,
12647             forceAll: forceAll,
12648             combo: this,
12649             cancel:false
12650         };
12651         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12652             return false;
12653         }
12654         q = qe.query;
12655         
12656         forceAll = qe.forceAll;
12657         if(forceAll === true || (q.length >= this.minChars)){
12658             
12659             this.hasQuery = true;
12660             
12661             if(this.lastQuery != q || this.alwaysQuery){
12662                 this.lastQuery = q;
12663                 if(this.mode == 'local'){
12664                     this.selectedIndex = -1;
12665                     if(forceAll){
12666                         this.store.clearFilter();
12667                     }else{
12668                         
12669                         if(this.specialFilter){
12670                             this.fireEvent('specialfilter', this);
12671                             this.onLoad();
12672                             return;
12673                         }
12674                         
12675                         this.store.filter(this.displayField, q);
12676                     }
12677                     
12678                     this.store.fireEvent("datachanged", this.store);
12679                     
12680                     this.onLoad();
12681                     
12682                     
12683                 }else{
12684                     
12685                     this.store.baseParams[this.queryParam] = q;
12686                     
12687                     var options = {params : this.getParams(q)};
12688                     
12689                     if(this.loadNext){
12690                         options.add = true;
12691                         options.params.start = this.page * this.pageSize;
12692                     }
12693                     
12694                     this.store.load(options);
12695                     
12696                     /*
12697                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12698                      *  we should expand the list on onLoad
12699                      *  so command out it
12700                      */
12701 //                    this.expand();
12702                 }
12703             }else{
12704                 this.selectedIndex = -1;
12705                 this.onLoad();   
12706             }
12707         }
12708         
12709         this.loadNext = false;
12710     },
12711     
12712     // private
12713     getParams : function(q){
12714         var p = {};
12715         //p[this.queryParam] = q;
12716         
12717         if(this.pageSize){
12718             p.start = 0;
12719             p.limit = this.pageSize;
12720         }
12721         return p;
12722     },
12723
12724     /**
12725      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12726      */
12727     collapse : function(){
12728         if(!this.isExpanded()){
12729             return;
12730         }
12731         
12732         this.list.hide();
12733         
12734         if(this.tickable){
12735             this.hasFocus = false;
12736             this.okBtn.hide();
12737             this.cancelBtn.hide();
12738             this.trigger.show();
12739             
12740             if(this.editable){
12741                 this.tickableInputEl().dom.value = '';
12742                 this.tickableInputEl().blur();
12743             }
12744             
12745         }
12746         
12747         Roo.get(document).un('mousedown', this.collapseIf, this);
12748         Roo.get(document).un('mousewheel', this.collapseIf, this);
12749         if (!this.editable) {
12750             Roo.get(document).un('keydown', this.listKeyPress, this);
12751         }
12752         this.fireEvent('collapse', this);
12753     },
12754
12755     // private
12756     collapseIf : function(e){
12757         var in_combo  = e.within(this.el);
12758         var in_list =  e.within(this.list);
12759         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12760         
12761         if (in_combo || in_list || is_list) {
12762             //e.stopPropagation();
12763             return;
12764         }
12765         
12766         if(this.tickable){
12767             this.onTickableFooterButtonClick(e, false, false);
12768         }
12769
12770         this.collapse();
12771         
12772     },
12773
12774     /**
12775      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12776      */
12777     expand : function(){
12778        
12779         if(this.isExpanded() || !this.hasFocus){
12780             return;
12781         }
12782         
12783         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12784         this.list.setWidth(lw);
12785         
12786         
12787          Roo.log('expand');
12788         
12789         this.list.show();
12790         
12791         this.restrictHeight();
12792         
12793         if(this.tickable){
12794             
12795             this.tickItems = Roo.apply([], this.item);
12796             
12797             this.okBtn.show();
12798             this.cancelBtn.show();
12799             this.trigger.hide();
12800             
12801             if(this.editable){
12802                 this.tickableInputEl().focus();
12803             }
12804             
12805         }
12806         
12807         Roo.get(document).on('mousedown', this.collapseIf, this);
12808         Roo.get(document).on('mousewheel', this.collapseIf, this);
12809         if (!this.editable) {
12810             Roo.get(document).on('keydown', this.listKeyPress, this);
12811         }
12812         
12813         this.fireEvent('expand', this);
12814     },
12815
12816     // private
12817     // Implements the default empty TriggerField.onTriggerClick function
12818     onTriggerClick : function(e)
12819     {
12820         Roo.log('trigger click');
12821         
12822         if(this.disabled || !this.triggerList){
12823             return;
12824         }
12825         
12826         this.page = 0;
12827         this.loadNext = false;
12828         
12829         if(this.isExpanded()){
12830             this.collapse();
12831             if (!this.blockFocus) {
12832                 this.inputEl().focus();
12833             }
12834             
12835         }else {
12836             this.hasFocus = true;
12837             if(this.triggerAction == 'all') {
12838                 this.doQuery(this.allQuery, true);
12839             } else {
12840                 this.doQuery(this.getRawValue());
12841             }
12842             if (!this.blockFocus) {
12843                 this.inputEl().focus();
12844             }
12845         }
12846     },
12847     
12848     onTickableTriggerClick : function(e)
12849     {
12850         if(this.disabled){
12851             return;
12852         }
12853         
12854         this.page = 0;
12855         this.loadNext = false;
12856         this.hasFocus = true;
12857         
12858         if(this.triggerAction == 'all') {
12859             this.doQuery(this.allQuery, true);
12860         } else {
12861             this.doQuery(this.getRawValue());
12862         }
12863     },
12864     
12865     onSearchFieldClick : function(e)
12866     {
12867         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12868             this.onTickableFooterButtonClick(e, false, false);
12869             return;
12870         }
12871         
12872         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12873             return;
12874         }
12875         
12876         this.page = 0;
12877         this.loadNext = false;
12878         this.hasFocus = true;
12879         
12880         if(this.triggerAction == 'all') {
12881             this.doQuery(this.allQuery, true);
12882         } else {
12883             this.doQuery(this.getRawValue());
12884         }
12885     },
12886     
12887     listKeyPress : function(e)
12888     {
12889         //Roo.log('listkeypress');
12890         // scroll to first matching element based on key pres..
12891         if (e.isSpecialKey()) {
12892             return false;
12893         }
12894         var k = String.fromCharCode(e.getKey()).toUpperCase();
12895         //Roo.log(k);
12896         var match  = false;
12897         var csel = this.view.getSelectedNodes();
12898         var cselitem = false;
12899         if (csel.length) {
12900             var ix = this.view.indexOf(csel[0]);
12901             cselitem  = this.store.getAt(ix);
12902             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12903                 cselitem = false;
12904             }
12905             
12906         }
12907         
12908         this.store.each(function(v) { 
12909             if (cselitem) {
12910                 // start at existing selection.
12911                 if (cselitem.id == v.id) {
12912                     cselitem = false;
12913                 }
12914                 return true;
12915             }
12916                 
12917             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12918                 match = this.store.indexOf(v);
12919                 return false;
12920             }
12921             return true;
12922         }, this);
12923         
12924         if (match === false) {
12925             return true; // no more action?
12926         }
12927         // scroll to?
12928         this.view.select(match);
12929         var sn = Roo.get(this.view.getSelectedNodes()[0])
12930         sn.scrollIntoView(sn.dom.parentNode, false);
12931     },
12932     
12933     onViewScroll : function(e, t){
12934         
12935         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){
12936             return;
12937         }
12938         
12939         this.hasQuery = true;
12940         
12941         this.loading = this.list.select('.loading', true).first();
12942         
12943         if(this.loading === null){
12944             this.list.createChild({
12945                 tag: 'div',
12946                 cls: 'loading select2-more-results select2-active',
12947                 html: 'Loading more results...'
12948             })
12949             
12950             this.loading = this.list.select('.loading', true).first();
12951             
12952             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12953             
12954             this.loading.hide();
12955         }
12956         
12957         this.loading.show();
12958         
12959         var _combo = this;
12960         
12961         this.page++;
12962         this.loadNext = true;
12963         
12964         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12965         
12966         return;
12967     },
12968     
12969     addItem : function(o)
12970     {   
12971         var dv = ''; // display value
12972         
12973         if (this.displayField) {
12974             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12975         } else {
12976             // this is an error condition!!!
12977             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12978         }
12979         
12980         if(!dv.length){
12981             return;
12982         }
12983         
12984         var choice = this.choices.createChild({
12985             tag: 'li',
12986             cls: 'select2-search-choice',
12987             cn: [
12988                 {
12989                     tag: 'div',
12990                     html: dv
12991                 },
12992                 {
12993                     tag: 'a',
12994                     href: '#',
12995                     cls: 'select2-search-choice-close',
12996                     tabindex: '-1'
12997                 }
12998             ]
12999             
13000         }, this.searchField);
13001         
13002         var close = choice.select('a.select2-search-choice-close', true).first()
13003         
13004         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13005         
13006         this.item.push(o);
13007         
13008         this.lastData = o;
13009         
13010         this.syncValue();
13011         
13012         this.inputEl().dom.value = '';
13013         
13014         this.validate();
13015     },
13016     
13017     onRemoveItem : function(e, _self, o)
13018     {
13019         e.preventDefault();
13020         
13021         this.lastItem = Roo.apply([], this.item);
13022         
13023         var index = this.item.indexOf(o.data) * 1;
13024         
13025         if( index < 0){
13026             Roo.log('not this item?!');
13027             return;
13028         }
13029         
13030         this.item.splice(index, 1);
13031         o.item.remove();
13032         
13033         this.syncValue();
13034         
13035         this.fireEvent('remove', this, e);
13036         
13037         this.validate();
13038         
13039     },
13040     
13041     syncValue : function()
13042     {
13043         if(!this.item.length){
13044             this.clearValue();
13045             return;
13046         }
13047             
13048         var value = [];
13049         var _this = this;
13050         Roo.each(this.item, function(i){
13051             if(_this.valueField){
13052                 value.push(i[_this.valueField]);
13053                 return;
13054             }
13055
13056             value.push(i);
13057         });
13058
13059         this.value = value.join(',');
13060
13061         if(this.hiddenField){
13062             this.hiddenField.dom.value = this.value;
13063         }
13064         
13065         this.store.fireEvent("datachanged", this.store);
13066     },
13067     
13068     clearItem : function()
13069     {
13070         if(!this.multiple){
13071             return;
13072         }
13073         
13074         this.item = [];
13075         
13076         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13077            c.remove();
13078         });
13079         
13080         this.syncValue();
13081         
13082         this.validate();
13083         
13084         if(this.tickable && !Roo.isTouch){
13085             this.view.refresh();
13086         }
13087     },
13088     
13089     inputEl: function ()
13090     {
13091         if(Roo.isTouch && this.mobileTouchView){
13092             return this.el.select('input.form-control',true).first();
13093         }
13094         
13095         if(this.tickable){
13096             return this.searchField;
13097         }
13098         
13099         return this.el.select('input.form-control',true).first();
13100     },
13101     
13102     
13103     onTickableFooterButtonClick : function(e, btn, el)
13104     {
13105         e.preventDefault();
13106         
13107         this.lastItem = Roo.apply([], this.item);
13108         
13109         if(btn && btn.name == 'cancel'){
13110             this.tickItems = Roo.apply([], this.item);
13111             this.collapse();
13112             return;
13113         }
13114         
13115         this.clearItem();
13116         
13117         var _this = this;
13118         
13119         Roo.each(this.tickItems, function(o){
13120             _this.addItem(o);
13121         });
13122         
13123         this.collapse();
13124         
13125     },
13126     
13127     validate : function()
13128     {
13129         var v = this.getRawValue();
13130         
13131         if(this.multiple){
13132             v = this.getValue();
13133         }
13134         
13135         if(this.disabled || this.allowBlank || v.length){
13136             this.markValid();
13137             return true;
13138         }
13139         
13140         this.markInvalid();
13141         return false;
13142     },
13143     
13144     tickableInputEl : function()
13145     {
13146         if(!this.tickable || !this.editable){
13147             return this.inputEl();
13148         }
13149         
13150         return this.inputEl().select('.select2-search-field-input', true).first();
13151     },
13152     
13153     
13154     getAutoCreateTouchView : function()
13155     {
13156         var id = Roo.id();
13157         
13158         var cfg = {
13159             cls: 'form-group' //input-group
13160         };
13161         
13162         var input =  {
13163             tag: 'input',
13164             id : id,
13165             type : this.inputType,
13166             cls : 'form-control x-combo-noedit',
13167             autocomplete: 'new-password',
13168             placeholder : this.placeholder || '',
13169             readonly : true
13170         };
13171         
13172         if (this.name) {
13173             input.name = this.name;
13174         }
13175         
13176         if (this.size) {
13177             input.cls += ' input-' + this.size;
13178         }
13179         
13180         if (this.disabled) {
13181             input.disabled = true;
13182         }
13183         
13184         var inputblock = {
13185             cls : '',
13186             cn : [
13187                 input
13188             ]
13189         };
13190         
13191         if(this.before){
13192             inputblock.cls += ' input-group';
13193             
13194             inputblock.cn.unshift({
13195                 tag :'span',
13196                 cls : 'input-group-addon',
13197                 html : this.before
13198             });
13199         }
13200         
13201         if(this.removable && !this.multiple){
13202             inputblock.cls += ' roo-removable';
13203             
13204             inputblock.cn.push({
13205                 tag: 'button',
13206                 html : 'x',
13207                 cls : 'roo-combo-removable-btn close'
13208             });
13209         }
13210
13211         if(this.hasFeedback && !this.allowBlank){
13212             
13213             inputblock.cls += ' has-feedback';
13214             
13215             inputblock.cn.push({
13216                 tag: 'span',
13217                 cls: 'glyphicon form-control-feedback'
13218             });
13219             
13220         }
13221         
13222         if (this.after) {
13223             
13224             inputblock.cls += (this.before) ? '' : ' input-group';
13225             
13226             inputblock.cn.push({
13227                 tag :'span',
13228                 cls : 'input-group-addon',
13229                 html : this.after
13230             });
13231         }
13232
13233         var box = {
13234             tag: 'div',
13235             cn: [
13236                 {
13237                     tag: 'input',
13238                     type : 'hidden',
13239                     cls: 'form-hidden-field'
13240                 },
13241                 inputblock
13242             ]
13243             
13244         };
13245         
13246         if(this.multiple){
13247             box = {
13248                 tag: 'div',
13249                 cn: [
13250                     {
13251                         tag: 'input',
13252                         type : 'hidden',
13253                         cls: 'form-hidden-field'
13254                     },
13255                     {
13256                         tag: 'ul',
13257                         cls: 'select2-choices',
13258                         cn:[
13259                             {
13260                                 tag: 'li',
13261                                 cls: 'select2-search-field',
13262                                 cn: [
13263
13264                                     inputblock
13265                                 ]
13266                             }
13267                         ]
13268                     }
13269                 ]
13270             }
13271         };
13272         
13273         var combobox = {
13274             cls: 'select2-container input-group',
13275             cn: [
13276                 box
13277             ]
13278         };
13279         
13280         if(this.multiple){
13281             combobox.cls += ' select2-container-multi';
13282         }
13283         
13284         var align = this.labelAlign || this.parentLabelAlign();
13285         
13286         cfg.cn = combobox;
13287         
13288         if(this.fieldLabel.length){
13289             
13290             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13291             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13292             
13293             cfg.cn = [
13294                 {
13295                     tag: 'label',
13296                     cls : 'control-label ' + lw,
13297                     html : this.fieldLabel
13298
13299                 },
13300                 {
13301                     cls : cw, 
13302                     cn: [
13303                         combobox
13304                     ]
13305                 }
13306             ];
13307         }
13308         
13309         var settings = this;
13310         
13311         ['xs','sm','md','lg'].map(function(size){
13312             if (settings[size]) {
13313                 cfg.cls += ' col-' + size + '-' + settings[size];
13314             }
13315         });
13316         
13317         return cfg;
13318     },
13319     
13320     initTouchView : function()
13321     {
13322         this.renderTouchView();
13323         
13324         this.touchViewEl.on('scroll', function(){
13325             this.el.dom.scrollTop = 0;
13326         }, this);
13327         
13328         this.inputEl().on("click", this.showTouchView, this);
13329         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13330         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13331         
13332         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13333         
13334         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13335         this.store.on('load', this.onTouchViewLoad, this);
13336         this.store.on('loadexception', this.onTouchViewLoadException, this);
13337         
13338         if(this.hiddenName){
13339             
13340             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13341             
13342             this.hiddenField.dom.value =
13343                 this.hiddenValue !== undefined ? this.hiddenValue :
13344                 this.value !== undefined ? this.value : '';
13345         
13346             this.el.dom.removeAttribute('name');
13347             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13348         }
13349         
13350         if(this.multiple){
13351             this.choices = this.el.select('ul.select2-choices', true).first();
13352             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13353         }
13354         
13355         if(this.removable && !this.multiple){
13356             var close = this.closeTriggerEl();
13357             if(close){
13358                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13359                 close.on('click', this.removeBtnClick, this, close);
13360             }
13361         }
13362         
13363         return;
13364         
13365         
13366     },
13367     
13368     renderTouchView : function()
13369     {
13370         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13371         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13372         
13373         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13374         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13375         
13376         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13377         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13378         this.touchViewBodyEl.setStyle('overflow', 'auto');
13379         
13380         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13381         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13382         
13383         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13384         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13385         
13386     },
13387     
13388     showTouchView : function()
13389     {
13390         this.touchViewHeaderEl.hide();
13391
13392         if(this.fieldLabel.length){
13393             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13394             this.touchViewHeaderEl.show();
13395         }
13396
13397         this.touchViewEl.show();
13398
13399         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13400         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13401
13402         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13403
13404         if(this.fieldLabel.length){
13405             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13406         }
13407         
13408         this.touchViewBodyEl.setHeight(bodyHeight);
13409
13410         if(this.animate){
13411             var _this = this;
13412             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13413         }else{
13414             this.touchViewEl.addClass('in');
13415         }
13416
13417         this.doTouchViewQuery();
13418         
13419     },
13420     
13421     hideTouchView : function()
13422     {
13423         this.touchViewEl.removeClass('in');
13424
13425         if(this.animate){
13426             var _this = this;
13427             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13428         }else{
13429             this.touchViewEl.setStyle('display', 'none');
13430         }
13431         
13432     },
13433     
13434     setTouchViewValue : function()
13435     {
13436         if(this.multiple){
13437             this.clearItem();
13438         
13439             var _this = this;
13440
13441             Roo.each(this.tickItems, function(o){
13442                 this.addItem(o);
13443             }, this);
13444         }
13445         
13446         this.hideTouchView();
13447     },
13448     
13449     doTouchViewQuery : function()
13450     {
13451         var qe = {
13452             query: '',
13453             forceAll: true,
13454             combo: this,
13455             cancel:false
13456         };
13457         
13458         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13459             return false;
13460         }
13461         
13462         if(!this.alwaysQuery || this.mode == 'local'){
13463             this.onTouchViewLoad();
13464             return;
13465         }
13466         
13467         this.store.load();
13468     },
13469     
13470     onTouchViewBeforeLoad : function(combo,opts)
13471     {
13472         return;
13473     },
13474
13475     // private
13476     onTouchViewLoad : function()
13477     {
13478         if(this.store.getCount() < 1){
13479             this.onTouchViewEmptyResults();
13480             return;
13481         }
13482         
13483         this.clearTouchView();
13484         
13485         var rawValue = this.getRawValue();
13486         
13487         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13488         
13489         this.tickItems = [];
13490         
13491         this.store.data.each(function(d, rowIndex){
13492             var row = this.touchViewListGroup.createChild(template);
13493             
13494             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13495                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13496             }
13497             
13498             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13499                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13500             }
13501             
13502             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13503                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13504                 this.tickItems.push(d.data);
13505             }
13506             
13507             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13508             
13509         }, this);
13510         
13511         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13512         
13513         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13514
13515         if(this.fieldLabel.length){
13516             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13517         }
13518
13519         var listHeight = this.touchViewListGroup.getHeight();
13520         
13521         var _this = this;
13522         
13523         if(firstChecked && listHeight > bodyHeight){
13524             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13525         }
13526         
13527     },
13528     
13529     onTouchViewLoadException : function()
13530     {
13531         this.hideTouchView();
13532     },
13533     
13534     onTouchViewEmptyResults : function()
13535     {
13536         this.clearTouchView();
13537         
13538         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13539         
13540         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13541         
13542     },
13543     
13544     clearTouchView : function()
13545     {
13546         this.touchViewListGroup.dom.innerHTML = '';
13547     },
13548     
13549     onTouchViewClick : function(e, el, o)
13550     {
13551         e.preventDefault();
13552         
13553         var row = o.row;
13554         var rowIndex = o.rowIndex;
13555         
13556         var r = this.store.getAt(rowIndex);
13557         
13558         if(!this.multiple){
13559             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13560                 c.dom.removeAttribute('checked');
13561             }, this);
13562             
13563             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13564         
13565             this.setFromData(r.data);
13566             
13567             var close = this.closeTriggerEl();
13568         
13569             if(close){
13570                 close.show();
13571             }
13572
13573             this.hideTouchView();
13574             
13575             this.fireEvent('select', this, r, rowIndex);
13576             
13577             return;
13578         }
13579         
13580         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13581             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13582             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13583             return;
13584         }
13585         
13586         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13587         this.addItem(r.data);
13588         this.tickItems.push(r.data);
13589         
13590     }
13591     
13592
13593     /** 
13594     * @cfg {Boolean} grow 
13595     * @hide 
13596     */
13597     /** 
13598     * @cfg {Number} growMin 
13599     * @hide 
13600     */
13601     /** 
13602     * @cfg {Number} growMax 
13603     * @hide 
13604     */
13605     /**
13606      * @hide
13607      * @method autoSize
13608      */
13609 });
13610
13611 Roo.apply(Roo.bootstrap.ComboBox,  {
13612     
13613     header : {
13614         tag: 'div',
13615         cls: 'modal-header',
13616         cn: [
13617             {
13618                 tag: 'h4',
13619                 cls: 'modal-title'
13620             }
13621         ]
13622     },
13623     
13624     body : {
13625         tag: 'div',
13626         cls: 'modal-body',
13627         cn: [
13628             {
13629                 tag: 'ul',
13630                 cls: 'list-group'
13631             }
13632         ]
13633     },
13634     
13635     listItemRadio : {
13636         tag: 'li',
13637         cls: 'list-group-item',
13638         cn: [
13639             {
13640                 tag: 'span',
13641                 cls: 'roo-combobox-list-group-item-value'
13642             },
13643             {
13644                 tag: 'div',
13645                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13646                 cn: [
13647                     {
13648                         tag: 'input',
13649                         type: 'radio'
13650                     },
13651                     {
13652                         tag: 'label'
13653                     }
13654                 ]
13655             }
13656         ]
13657     },
13658     
13659     listItemCheckbox : {
13660         tag: 'li',
13661         cls: 'list-group-item',
13662         cn: [
13663             {
13664                 tag: 'span',
13665                 cls: 'roo-combobox-list-group-item-value'
13666             },
13667             {
13668                 tag: 'div',
13669                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13670                 cn: [
13671                     {
13672                         tag: 'input',
13673                         type: 'checkbox'
13674                     },
13675                     {
13676                         tag: 'label'
13677                     }
13678                 ]
13679             }
13680         ]
13681     },
13682     
13683     emptyResult : {
13684         tag: 'div',
13685         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13686     },
13687     
13688     footer : {
13689         tag: 'div',
13690         cls: 'modal-footer',
13691         cn: [
13692             {
13693                 tag: 'div',
13694                 cls: 'row',
13695                 cn: [
13696                     {
13697                         tag: 'div',
13698                         cls: 'col-xs-6 text-left',
13699                         cn: {
13700                             tag: 'button',
13701                             cls: 'btn btn-danger roo-touch-view-cancel',
13702                             html: 'Cancel'
13703                         }
13704                     },
13705                     {
13706                         tag: 'div',
13707                         cls: 'col-xs-6 text-right',
13708                         cn: {
13709                             tag: 'button',
13710                             cls: 'btn btn-success roo-touch-view-ok',
13711                             html: 'OK'
13712                         }
13713                     }
13714                 ]
13715             }
13716         ]
13717         
13718     }
13719 });
13720
13721 Roo.apply(Roo.bootstrap.ComboBox,  {
13722     
13723     touchViewTemplate : {
13724         tag: 'div',
13725         cls: 'modal fade roo-combobox-touch-view',
13726         cn: [
13727             {
13728                 tag: 'div',
13729                 cls: 'modal-dialog',
13730                 cn: [
13731                     {
13732                         tag: 'div',
13733                         cls: 'modal-content',
13734                         cn: [
13735                             Roo.bootstrap.ComboBox.header,
13736                             Roo.bootstrap.ComboBox.body,
13737                             Roo.bootstrap.ComboBox.footer
13738                         ]
13739                     }
13740                 ]
13741             }
13742         ]
13743     }
13744 });/*
13745  * Based on:
13746  * Ext JS Library 1.1.1
13747  * Copyright(c) 2006-2007, Ext JS, LLC.
13748  *
13749  * Originally Released Under LGPL - original licence link has changed is not relivant.
13750  *
13751  * Fork - LGPL
13752  * <script type="text/javascript">
13753  */
13754
13755 /**
13756  * @class Roo.View
13757  * @extends Roo.util.Observable
13758  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13759  * This class also supports single and multi selection modes. <br>
13760  * Create a data model bound view:
13761  <pre><code>
13762  var store = new Roo.data.Store(...);
13763
13764  var view = new Roo.View({
13765     el : "my-element",
13766     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13767  
13768     singleSelect: true,
13769     selectedClass: "ydataview-selected",
13770     store: store
13771  });
13772
13773  // listen for node click?
13774  view.on("click", function(vw, index, node, e){
13775  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13776  });
13777
13778  // load XML data
13779  dataModel.load("foobar.xml");
13780  </code></pre>
13781  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13782  * <br><br>
13783  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13784  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13785  * 
13786  * Note: old style constructor is still suported (container, template, config)
13787  * 
13788  * @constructor
13789  * Create a new View
13790  * @param {Object} config The config object
13791  * 
13792  */
13793 Roo.View = function(config, depreciated_tpl, depreciated_config){
13794     
13795     this.parent = false;
13796     
13797     if (typeof(depreciated_tpl) == 'undefined') {
13798         // new way.. - universal constructor.
13799         Roo.apply(this, config);
13800         this.el  = Roo.get(this.el);
13801     } else {
13802         // old format..
13803         this.el  = Roo.get(config);
13804         this.tpl = depreciated_tpl;
13805         Roo.apply(this, depreciated_config);
13806     }
13807     this.wrapEl  = this.el.wrap().wrap();
13808     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13809     
13810     
13811     if(typeof(this.tpl) == "string"){
13812         this.tpl = new Roo.Template(this.tpl);
13813     } else {
13814         // support xtype ctors..
13815         this.tpl = new Roo.factory(this.tpl, Roo);
13816     }
13817     
13818     
13819     this.tpl.compile();
13820     
13821     /** @private */
13822     this.addEvents({
13823         /**
13824          * @event beforeclick
13825          * Fires before a click is processed. Returns false to cancel the default action.
13826          * @param {Roo.View} this
13827          * @param {Number} index The index of the target node
13828          * @param {HTMLElement} node The target node
13829          * @param {Roo.EventObject} e The raw event object
13830          */
13831             "beforeclick" : true,
13832         /**
13833          * @event click
13834          * Fires when a template node is clicked.
13835          * @param {Roo.View} this
13836          * @param {Number} index The index of the target node
13837          * @param {HTMLElement} node The target node
13838          * @param {Roo.EventObject} e The raw event object
13839          */
13840             "click" : true,
13841         /**
13842          * @event dblclick
13843          * Fires when a template node is double clicked.
13844          * @param {Roo.View} this
13845          * @param {Number} index The index of the target node
13846          * @param {HTMLElement} node The target node
13847          * @param {Roo.EventObject} e The raw event object
13848          */
13849             "dblclick" : true,
13850         /**
13851          * @event contextmenu
13852          * Fires when a template node is right clicked.
13853          * @param {Roo.View} this
13854          * @param {Number} index The index of the target node
13855          * @param {HTMLElement} node The target node
13856          * @param {Roo.EventObject} e The raw event object
13857          */
13858             "contextmenu" : true,
13859         /**
13860          * @event selectionchange
13861          * Fires when the selected nodes change.
13862          * @param {Roo.View} this
13863          * @param {Array} selections Array of the selected nodes
13864          */
13865             "selectionchange" : true,
13866     
13867         /**
13868          * @event beforeselect
13869          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13870          * @param {Roo.View} this
13871          * @param {HTMLElement} node The node to be selected
13872          * @param {Array} selections Array of currently selected nodes
13873          */
13874             "beforeselect" : true,
13875         /**
13876          * @event preparedata
13877          * Fires on every row to render, to allow you to change the data.
13878          * @param {Roo.View} this
13879          * @param {Object} data to be rendered (change this)
13880          */
13881           "preparedata" : true
13882           
13883           
13884         });
13885
13886
13887
13888     this.el.on({
13889         "click": this.onClick,
13890         "dblclick": this.onDblClick,
13891         "contextmenu": this.onContextMenu,
13892         scope:this
13893     });
13894
13895     this.selections = [];
13896     this.nodes = [];
13897     this.cmp = new Roo.CompositeElementLite([]);
13898     if(this.store){
13899         this.store = Roo.factory(this.store, Roo.data);
13900         this.setStore(this.store, true);
13901     }
13902     
13903     if ( this.footer && this.footer.xtype) {
13904            
13905          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13906         
13907         this.footer.dataSource = this.store;
13908         this.footer.container = fctr;
13909         this.footer = Roo.factory(this.footer, Roo);
13910         fctr.insertFirst(this.el);
13911         
13912         // this is a bit insane - as the paging toolbar seems to detach the el..
13913 //        dom.parentNode.parentNode.parentNode
13914          // they get detached?
13915     }
13916     
13917     
13918     Roo.View.superclass.constructor.call(this);
13919     
13920     
13921 };
13922
13923 Roo.extend(Roo.View, Roo.util.Observable, {
13924     
13925      /**
13926      * @cfg {Roo.data.Store} store Data store to load data from.
13927      */
13928     store : false,
13929     
13930     /**
13931      * @cfg {String|Roo.Element} el The container element.
13932      */
13933     el : '',
13934     
13935     /**
13936      * @cfg {String|Roo.Template} tpl The template used by this View 
13937      */
13938     tpl : false,
13939     /**
13940      * @cfg {String} dataName the named area of the template to use as the data area
13941      *                          Works with domtemplates roo-name="name"
13942      */
13943     dataName: false,
13944     /**
13945      * @cfg {String} selectedClass The css class to add to selected nodes
13946      */
13947     selectedClass : "x-view-selected",
13948      /**
13949      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13950      */
13951     emptyText : "",
13952     
13953     /**
13954      * @cfg {String} text to display on mask (default Loading)
13955      */
13956     mask : false,
13957     /**
13958      * @cfg {Boolean} multiSelect Allow multiple selection
13959      */
13960     multiSelect : false,
13961     /**
13962      * @cfg {Boolean} singleSelect Allow single selection
13963      */
13964     singleSelect:  false,
13965     
13966     /**
13967      * @cfg {Boolean} toggleSelect - selecting 
13968      */
13969     toggleSelect : false,
13970     
13971     /**
13972      * @cfg {Boolean} tickable - selecting 
13973      */
13974     tickable : false,
13975     
13976     /**
13977      * Returns the element this view is bound to.
13978      * @return {Roo.Element}
13979      */
13980     getEl : function(){
13981         return this.wrapEl;
13982     },
13983     
13984     
13985
13986     /**
13987      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13988      */
13989     refresh : function(){
13990         //Roo.log('refresh');
13991         var t = this.tpl;
13992         
13993         // if we are using something like 'domtemplate', then
13994         // the what gets used is:
13995         // t.applySubtemplate(NAME, data, wrapping data..)
13996         // the outer template then get' applied with
13997         //     the store 'extra data'
13998         // and the body get's added to the
13999         //      roo-name="data" node?
14000         //      <span class='roo-tpl-{name}'></span> ?????
14001         
14002         
14003         
14004         this.clearSelections();
14005         this.el.update("");
14006         var html = [];
14007         var records = this.store.getRange();
14008         if(records.length < 1) {
14009             
14010             // is this valid??  = should it render a template??
14011             
14012             this.el.update(this.emptyText);
14013             return;
14014         }
14015         var el = this.el;
14016         if (this.dataName) {
14017             this.el.update(t.apply(this.store.meta)); //????
14018             el = this.el.child('.roo-tpl-' + this.dataName);
14019         }
14020         
14021         for(var i = 0, len = records.length; i < len; i++){
14022             var data = this.prepareData(records[i].data, i, records[i]);
14023             this.fireEvent("preparedata", this, data, i, records[i]);
14024             
14025             var d = Roo.apply({}, data);
14026             
14027             if(this.tickable){
14028                 Roo.apply(d, {'roo-id' : Roo.id()});
14029                 
14030                 var _this = this;
14031             
14032                 Roo.each(this.parent.item, function(item){
14033                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14034                         return;
14035                     }
14036                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14037                 });
14038             }
14039             
14040             html[html.length] = Roo.util.Format.trim(
14041                 this.dataName ?
14042                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14043                     t.apply(d)
14044             );
14045         }
14046         
14047         
14048         
14049         el.update(html.join(""));
14050         this.nodes = el.dom.childNodes;
14051         this.updateIndexes(0);
14052     },
14053     
14054
14055     /**
14056      * Function to override to reformat the data that is sent to
14057      * the template for each node.
14058      * DEPRICATED - use the preparedata event handler.
14059      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14060      * a JSON object for an UpdateManager bound view).
14061      */
14062     prepareData : function(data, index, record)
14063     {
14064         this.fireEvent("preparedata", this, data, index, record);
14065         return data;
14066     },
14067
14068     onUpdate : function(ds, record){
14069         // Roo.log('on update');   
14070         this.clearSelections();
14071         var index = this.store.indexOf(record);
14072         var n = this.nodes[index];
14073         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14074         n.parentNode.removeChild(n);
14075         this.updateIndexes(index, index);
14076     },
14077
14078     
14079     
14080 // --------- FIXME     
14081     onAdd : function(ds, records, index)
14082     {
14083         //Roo.log(['on Add', ds, records, index] );        
14084         this.clearSelections();
14085         if(this.nodes.length == 0){
14086             this.refresh();
14087             return;
14088         }
14089         var n = this.nodes[index];
14090         for(var i = 0, len = records.length; i < len; i++){
14091             var d = this.prepareData(records[i].data, i, records[i]);
14092             if(n){
14093                 this.tpl.insertBefore(n, d);
14094             }else{
14095                 
14096                 this.tpl.append(this.el, d);
14097             }
14098         }
14099         this.updateIndexes(index);
14100     },
14101
14102     onRemove : function(ds, record, index){
14103        // Roo.log('onRemove');
14104         this.clearSelections();
14105         var el = this.dataName  ?
14106             this.el.child('.roo-tpl-' + this.dataName) :
14107             this.el; 
14108         
14109         el.dom.removeChild(this.nodes[index]);
14110         this.updateIndexes(index);
14111     },
14112
14113     /**
14114      * Refresh an individual node.
14115      * @param {Number} index
14116      */
14117     refreshNode : function(index){
14118         this.onUpdate(this.store, this.store.getAt(index));
14119     },
14120
14121     updateIndexes : function(startIndex, endIndex){
14122         var ns = this.nodes;
14123         startIndex = startIndex || 0;
14124         endIndex = endIndex || ns.length - 1;
14125         for(var i = startIndex; i <= endIndex; i++){
14126             ns[i].nodeIndex = i;
14127         }
14128     },
14129
14130     /**
14131      * Changes the data store this view uses and refresh the view.
14132      * @param {Store} store
14133      */
14134     setStore : function(store, initial){
14135         if(!initial && this.store){
14136             this.store.un("datachanged", this.refresh);
14137             this.store.un("add", this.onAdd);
14138             this.store.un("remove", this.onRemove);
14139             this.store.un("update", this.onUpdate);
14140             this.store.un("clear", this.refresh);
14141             this.store.un("beforeload", this.onBeforeLoad);
14142             this.store.un("load", this.onLoad);
14143             this.store.un("loadexception", this.onLoad);
14144         }
14145         if(store){
14146           
14147             store.on("datachanged", this.refresh, this);
14148             store.on("add", this.onAdd, this);
14149             store.on("remove", this.onRemove, this);
14150             store.on("update", this.onUpdate, this);
14151             store.on("clear", this.refresh, this);
14152             store.on("beforeload", this.onBeforeLoad, this);
14153             store.on("load", this.onLoad, this);
14154             store.on("loadexception", this.onLoad, this);
14155         }
14156         
14157         if(store){
14158             this.refresh();
14159         }
14160     },
14161     /**
14162      * onbeforeLoad - masks the loading area.
14163      *
14164      */
14165     onBeforeLoad : function(store,opts)
14166     {
14167          //Roo.log('onBeforeLoad');   
14168         if (!opts.add) {
14169             this.el.update("");
14170         }
14171         this.el.mask(this.mask ? this.mask : "Loading" ); 
14172     },
14173     onLoad : function ()
14174     {
14175         this.el.unmask();
14176     },
14177     
14178
14179     /**
14180      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14181      * @param {HTMLElement} node
14182      * @return {HTMLElement} The template node
14183      */
14184     findItemFromChild : function(node){
14185         var el = this.dataName  ?
14186             this.el.child('.roo-tpl-' + this.dataName,true) :
14187             this.el.dom; 
14188         
14189         if(!node || node.parentNode == el){
14190                     return node;
14191             }
14192             var p = node.parentNode;
14193             while(p && p != el){
14194             if(p.parentNode == el){
14195                 return p;
14196             }
14197             p = p.parentNode;
14198         }
14199             return null;
14200     },
14201
14202     /** @ignore */
14203     onClick : function(e){
14204         var item = this.findItemFromChild(e.getTarget());
14205         if(item){
14206             var index = this.indexOf(item);
14207             if(this.onItemClick(item, index, e) !== false){
14208                 this.fireEvent("click", this, index, item, e);
14209             }
14210         }else{
14211             this.clearSelections();
14212         }
14213     },
14214
14215     /** @ignore */
14216     onContextMenu : function(e){
14217         var item = this.findItemFromChild(e.getTarget());
14218         if(item){
14219             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14220         }
14221     },
14222
14223     /** @ignore */
14224     onDblClick : function(e){
14225         var item = this.findItemFromChild(e.getTarget());
14226         if(item){
14227             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14228         }
14229     },
14230
14231     onItemClick : function(item, index, e)
14232     {
14233         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14234             return false;
14235         }
14236         if (this.toggleSelect) {
14237             var m = this.isSelected(item) ? 'unselect' : 'select';
14238             //Roo.log(m);
14239             var _t = this;
14240             _t[m](item, true, false);
14241             return true;
14242         }
14243         if(this.multiSelect || this.singleSelect){
14244             if(this.multiSelect && e.shiftKey && this.lastSelection){
14245                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14246             }else{
14247                 this.select(item, this.multiSelect && e.ctrlKey);
14248                 this.lastSelection = item;
14249             }
14250             
14251             if(!this.tickable){
14252                 e.preventDefault();
14253             }
14254             
14255         }
14256         return true;
14257     },
14258
14259     /**
14260      * Get the number of selected nodes.
14261      * @return {Number}
14262      */
14263     getSelectionCount : function(){
14264         return this.selections.length;
14265     },
14266
14267     /**
14268      * Get the currently selected nodes.
14269      * @return {Array} An array of HTMLElements
14270      */
14271     getSelectedNodes : function(){
14272         return this.selections;
14273     },
14274
14275     /**
14276      * Get the indexes of the selected nodes.
14277      * @return {Array}
14278      */
14279     getSelectedIndexes : function(){
14280         var indexes = [], s = this.selections;
14281         for(var i = 0, len = s.length; i < len; i++){
14282             indexes.push(s[i].nodeIndex);
14283         }
14284         return indexes;
14285     },
14286
14287     /**
14288      * Clear all selections
14289      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14290      */
14291     clearSelections : function(suppressEvent){
14292         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14293             this.cmp.elements = this.selections;
14294             this.cmp.removeClass(this.selectedClass);
14295             this.selections = [];
14296             if(!suppressEvent){
14297                 this.fireEvent("selectionchange", this, this.selections);
14298             }
14299         }
14300     },
14301
14302     /**
14303      * Returns true if the passed node is selected
14304      * @param {HTMLElement/Number} node The node or node index
14305      * @return {Boolean}
14306      */
14307     isSelected : function(node){
14308         var s = this.selections;
14309         if(s.length < 1){
14310             return false;
14311         }
14312         node = this.getNode(node);
14313         return s.indexOf(node) !== -1;
14314     },
14315
14316     /**
14317      * Selects nodes.
14318      * @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
14319      * @param {Boolean} keepExisting (optional) true to keep existing selections
14320      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14321      */
14322     select : function(nodeInfo, keepExisting, suppressEvent){
14323         if(nodeInfo instanceof Array){
14324             if(!keepExisting){
14325                 this.clearSelections(true);
14326             }
14327             for(var i = 0, len = nodeInfo.length; i < len; i++){
14328                 this.select(nodeInfo[i], true, true);
14329             }
14330             return;
14331         } 
14332         var node = this.getNode(nodeInfo);
14333         if(!node || this.isSelected(node)){
14334             return; // already selected.
14335         }
14336         if(!keepExisting){
14337             this.clearSelections(true);
14338         }
14339         
14340         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14341             Roo.fly(node).addClass(this.selectedClass);
14342             this.selections.push(node);
14343             if(!suppressEvent){
14344                 this.fireEvent("selectionchange", this, this.selections);
14345             }
14346         }
14347         
14348         
14349     },
14350       /**
14351      * Unselects nodes.
14352      * @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
14353      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14354      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14355      */
14356     unselect : function(nodeInfo, keepExisting, suppressEvent)
14357     {
14358         if(nodeInfo instanceof Array){
14359             Roo.each(this.selections, function(s) {
14360                 this.unselect(s, nodeInfo);
14361             }, this);
14362             return;
14363         }
14364         var node = this.getNode(nodeInfo);
14365         if(!node || !this.isSelected(node)){
14366             //Roo.log("not selected");
14367             return; // not selected.
14368         }
14369         // fireevent???
14370         var ns = [];
14371         Roo.each(this.selections, function(s) {
14372             if (s == node ) {
14373                 Roo.fly(node).removeClass(this.selectedClass);
14374
14375                 return;
14376             }
14377             ns.push(s);
14378         },this);
14379         
14380         this.selections= ns;
14381         this.fireEvent("selectionchange", this, this.selections);
14382     },
14383
14384     /**
14385      * Gets a template node.
14386      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14387      * @return {HTMLElement} The node or null if it wasn't found
14388      */
14389     getNode : function(nodeInfo){
14390         if(typeof nodeInfo == "string"){
14391             return document.getElementById(nodeInfo);
14392         }else if(typeof nodeInfo == "number"){
14393             return this.nodes[nodeInfo];
14394         }
14395         return nodeInfo;
14396     },
14397
14398     /**
14399      * Gets a range template nodes.
14400      * @param {Number} startIndex
14401      * @param {Number} endIndex
14402      * @return {Array} An array of nodes
14403      */
14404     getNodes : function(start, end){
14405         var ns = this.nodes;
14406         start = start || 0;
14407         end = typeof end == "undefined" ? ns.length - 1 : end;
14408         var nodes = [];
14409         if(start <= end){
14410             for(var i = start; i <= end; i++){
14411                 nodes.push(ns[i]);
14412             }
14413         } else{
14414             for(var i = start; i >= end; i--){
14415                 nodes.push(ns[i]);
14416             }
14417         }
14418         return nodes;
14419     },
14420
14421     /**
14422      * Finds the index of the passed node
14423      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14424      * @return {Number} The index of the node or -1
14425      */
14426     indexOf : function(node){
14427         node = this.getNode(node);
14428         if(typeof node.nodeIndex == "number"){
14429             return node.nodeIndex;
14430         }
14431         var ns = this.nodes;
14432         for(var i = 0, len = ns.length; i < len; i++){
14433             if(ns[i] == node){
14434                 return i;
14435             }
14436         }
14437         return -1;
14438     }
14439 });
14440 /*
14441  * - LGPL
14442  *
14443  * based on jquery fullcalendar
14444  * 
14445  */
14446
14447 Roo.bootstrap = Roo.bootstrap || {};
14448 /**
14449  * @class Roo.bootstrap.Calendar
14450  * @extends Roo.bootstrap.Component
14451  * Bootstrap Calendar class
14452  * @cfg {Boolean} loadMask (true|false) default false
14453  * @cfg {Object} header generate the user specific header of the calendar, default false
14454
14455  * @constructor
14456  * Create a new Container
14457  * @param {Object} config The config object
14458  */
14459
14460
14461
14462 Roo.bootstrap.Calendar = function(config){
14463     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14464      this.addEvents({
14465         /**
14466              * @event select
14467              * Fires when a date is selected
14468              * @param {DatePicker} this
14469              * @param {Date} date The selected date
14470              */
14471         'select': true,
14472         /**
14473              * @event monthchange
14474              * Fires when the displayed month changes 
14475              * @param {DatePicker} this
14476              * @param {Date} date The selected month
14477              */
14478         'monthchange': true,
14479         /**
14480              * @event evententer
14481              * Fires when mouse over an event
14482              * @param {Calendar} this
14483              * @param {event} Event
14484              */
14485         'evententer': true,
14486         /**
14487              * @event eventleave
14488              * Fires when the mouse leaves an
14489              * @param {Calendar} this
14490              * @param {event}
14491              */
14492         'eventleave': true,
14493         /**
14494              * @event eventclick
14495              * Fires when the mouse click an
14496              * @param {Calendar} this
14497              * @param {event}
14498              */
14499         'eventclick': true
14500         
14501     });
14502
14503 };
14504
14505 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14506     
14507      /**
14508      * @cfg {Number} startDay
14509      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14510      */
14511     startDay : 0,
14512     
14513     loadMask : false,
14514     
14515     header : false,
14516       
14517     getAutoCreate : function(){
14518         
14519         
14520         var fc_button = function(name, corner, style, content ) {
14521             return Roo.apply({},{
14522                 tag : 'span',
14523                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14524                          (corner.length ?
14525                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14526                             ''
14527                         ),
14528                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14529                 unselectable: 'on'
14530             });
14531         };
14532         
14533         var header = {};
14534         
14535         if(!this.header){
14536             header = {
14537                 tag : 'table',
14538                 cls : 'fc-header',
14539                 style : 'width:100%',
14540                 cn : [
14541                     {
14542                         tag: 'tr',
14543                         cn : [
14544                             {
14545                                 tag : 'td',
14546                                 cls : 'fc-header-left',
14547                                 cn : [
14548                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14549                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14550                                     { tag: 'span', cls: 'fc-header-space' },
14551                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14552
14553
14554                                 ]
14555                             },
14556
14557                             {
14558                                 tag : 'td',
14559                                 cls : 'fc-header-center',
14560                                 cn : [
14561                                     {
14562                                         tag: 'span',
14563                                         cls: 'fc-header-title',
14564                                         cn : {
14565                                             tag: 'H2',
14566                                             html : 'month / year'
14567                                         }
14568                                     }
14569
14570                                 ]
14571                             },
14572                             {
14573                                 tag : 'td',
14574                                 cls : 'fc-header-right',
14575                                 cn : [
14576                               /*      fc_button('month', 'left', '', 'month' ),
14577                                     fc_button('week', '', '', 'week' ),
14578                                     fc_button('day', 'right', '', 'day' )
14579                                 */    
14580
14581                                 ]
14582                             }
14583
14584                         ]
14585                     }
14586                 ]
14587             };
14588         }
14589         
14590         header = this.header;
14591         
14592        
14593         var cal_heads = function() {
14594             var ret = [];
14595             // fixme - handle this.
14596             
14597             for (var i =0; i < Date.dayNames.length; i++) {
14598                 var d = Date.dayNames[i];
14599                 ret.push({
14600                     tag: 'th',
14601                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14602                     html : d.substring(0,3)
14603                 });
14604                 
14605             }
14606             ret[0].cls += ' fc-first';
14607             ret[6].cls += ' fc-last';
14608             return ret;
14609         };
14610         var cal_cell = function(n) {
14611             return  {
14612                 tag: 'td',
14613                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14614                 cn : [
14615                     {
14616                         cn : [
14617                             {
14618                                 cls: 'fc-day-number',
14619                                 html: 'D'
14620                             },
14621                             {
14622                                 cls: 'fc-day-content',
14623                              
14624                                 cn : [
14625                                      {
14626                                         style: 'position: relative;' // height: 17px;
14627                                     }
14628                                 ]
14629                             }
14630                             
14631                             
14632                         ]
14633                     }
14634                 ]
14635                 
14636             }
14637         };
14638         var cal_rows = function() {
14639             
14640             var ret = [];
14641             for (var r = 0; r < 6; r++) {
14642                 var row= {
14643                     tag : 'tr',
14644                     cls : 'fc-week',
14645                     cn : []
14646                 };
14647                 
14648                 for (var i =0; i < Date.dayNames.length; i++) {
14649                     var d = Date.dayNames[i];
14650                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14651
14652                 }
14653                 row.cn[0].cls+=' fc-first';
14654                 row.cn[0].cn[0].style = 'min-height:90px';
14655                 row.cn[6].cls+=' fc-last';
14656                 ret.push(row);
14657                 
14658             }
14659             ret[0].cls += ' fc-first';
14660             ret[4].cls += ' fc-prev-last';
14661             ret[5].cls += ' fc-last';
14662             return ret;
14663             
14664         };
14665         
14666         var cal_table = {
14667             tag: 'table',
14668             cls: 'fc-border-separate',
14669             style : 'width:100%',
14670             cellspacing  : 0,
14671             cn : [
14672                 { 
14673                     tag: 'thead',
14674                     cn : [
14675                         { 
14676                             tag: 'tr',
14677                             cls : 'fc-first fc-last',
14678                             cn : cal_heads()
14679                         }
14680                     ]
14681                 },
14682                 { 
14683                     tag: 'tbody',
14684                     cn : cal_rows()
14685                 }
14686                   
14687             ]
14688         };
14689          
14690          var cfg = {
14691             cls : 'fc fc-ltr',
14692             cn : [
14693                 header,
14694                 {
14695                     cls : 'fc-content',
14696                     style : "position: relative;",
14697                     cn : [
14698                         {
14699                             cls : 'fc-view fc-view-month fc-grid',
14700                             style : 'position: relative',
14701                             unselectable : 'on',
14702                             cn : [
14703                                 {
14704                                     cls : 'fc-event-container',
14705                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14706                                 },
14707                                 cal_table
14708                             ]
14709                         }
14710                     ]
14711     
14712                 }
14713            ] 
14714             
14715         };
14716         
14717          
14718         
14719         return cfg;
14720     },
14721     
14722     
14723     initEvents : function()
14724     {
14725         if(!this.store){
14726             throw "can not find store for calendar";
14727         }
14728         
14729         var mark = {
14730             tag: "div",
14731             cls:"x-dlg-mask",
14732             style: "text-align:center",
14733             cn: [
14734                 {
14735                     tag: "div",
14736                     style: "background-color:white;width:50%;margin:250 auto",
14737                     cn: [
14738                         {
14739                             tag: "img",
14740                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14741                         },
14742                         {
14743                             tag: "span",
14744                             html: "Loading"
14745                         }
14746                         
14747                     ]
14748                 }
14749             ]
14750         }
14751         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14752         
14753         var size = this.el.select('.fc-content', true).first().getSize();
14754         this.maskEl.setSize(size.width, size.height);
14755         this.maskEl.enableDisplayMode("block");
14756         if(!this.loadMask){
14757             this.maskEl.hide();
14758         }
14759         
14760         this.store = Roo.factory(this.store, Roo.data);
14761         this.store.on('load', this.onLoad, this);
14762         this.store.on('beforeload', this.onBeforeLoad, this);
14763         
14764         this.resize();
14765         
14766         this.cells = this.el.select('.fc-day',true);
14767         //Roo.log(this.cells);
14768         this.textNodes = this.el.query('.fc-day-number');
14769         this.cells.addClassOnOver('fc-state-hover');
14770         
14771         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14772         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14773         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14774         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14775         
14776         this.on('monthchange', this.onMonthChange, this);
14777         
14778         this.update(new Date().clearTime());
14779     },
14780     
14781     resize : function() {
14782         var sz  = this.el.getSize();
14783         
14784         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14785         this.el.select('.fc-day-content div',true).setHeight(34);
14786     },
14787     
14788     
14789     // private
14790     showPrevMonth : function(e){
14791         this.update(this.activeDate.add("mo", -1));
14792     },
14793     showToday : function(e){
14794         this.update(new Date().clearTime());
14795     },
14796     // private
14797     showNextMonth : function(e){
14798         this.update(this.activeDate.add("mo", 1));
14799     },
14800
14801     // private
14802     showPrevYear : function(){
14803         this.update(this.activeDate.add("y", -1));
14804     },
14805
14806     // private
14807     showNextYear : function(){
14808         this.update(this.activeDate.add("y", 1));
14809     },
14810
14811     
14812    // private
14813     update : function(date)
14814     {
14815         var vd = this.activeDate;
14816         this.activeDate = date;
14817 //        if(vd && this.el){
14818 //            var t = date.getTime();
14819 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14820 //                Roo.log('using add remove');
14821 //                
14822 //                this.fireEvent('monthchange', this, date);
14823 //                
14824 //                this.cells.removeClass("fc-state-highlight");
14825 //                this.cells.each(function(c){
14826 //                   if(c.dateValue == t){
14827 //                       c.addClass("fc-state-highlight");
14828 //                       setTimeout(function(){
14829 //                            try{c.dom.firstChild.focus();}catch(e){}
14830 //                       }, 50);
14831 //                       return false;
14832 //                   }
14833 //                   return true;
14834 //                });
14835 //                return;
14836 //            }
14837 //        }
14838         
14839         var days = date.getDaysInMonth();
14840         
14841         var firstOfMonth = date.getFirstDateOfMonth();
14842         var startingPos = firstOfMonth.getDay()-this.startDay;
14843         
14844         if(startingPos < this.startDay){
14845             startingPos += 7;
14846         }
14847         
14848         var pm = date.add(Date.MONTH, -1);
14849         var prevStart = pm.getDaysInMonth()-startingPos;
14850 //        
14851         this.cells = this.el.select('.fc-day',true);
14852         this.textNodes = this.el.query('.fc-day-number');
14853         this.cells.addClassOnOver('fc-state-hover');
14854         
14855         var cells = this.cells.elements;
14856         var textEls = this.textNodes;
14857         
14858         Roo.each(cells, function(cell){
14859             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14860         });
14861         
14862         days += startingPos;
14863
14864         // convert everything to numbers so it's fast
14865         var day = 86400000;
14866         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14867         //Roo.log(d);
14868         //Roo.log(pm);
14869         //Roo.log(prevStart);
14870         
14871         var today = new Date().clearTime().getTime();
14872         var sel = date.clearTime().getTime();
14873         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14874         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14875         var ddMatch = this.disabledDatesRE;
14876         var ddText = this.disabledDatesText;
14877         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14878         var ddaysText = this.disabledDaysText;
14879         var format = this.format;
14880         
14881         var setCellClass = function(cal, cell){
14882             cell.row = 0;
14883             cell.events = [];
14884             cell.more = [];
14885             //Roo.log('set Cell Class');
14886             cell.title = "";
14887             var t = d.getTime();
14888             
14889             //Roo.log(d);
14890             
14891             cell.dateValue = t;
14892             if(t == today){
14893                 cell.className += " fc-today";
14894                 cell.className += " fc-state-highlight";
14895                 cell.title = cal.todayText;
14896             }
14897             if(t == sel){
14898                 // disable highlight in other month..
14899                 //cell.className += " fc-state-highlight";
14900                 
14901             }
14902             // disabling
14903             if(t < min) {
14904                 cell.className = " fc-state-disabled";
14905                 cell.title = cal.minText;
14906                 return;
14907             }
14908             if(t > max) {
14909                 cell.className = " fc-state-disabled";
14910                 cell.title = cal.maxText;
14911                 return;
14912             }
14913             if(ddays){
14914                 if(ddays.indexOf(d.getDay()) != -1){
14915                     cell.title = ddaysText;
14916                     cell.className = " fc-state-disabled";
14917                 }
14918             }
14919             if(ddMatch && format){
14920                 var fvalue = d.dateFormat(format);
14921                 if(ddMatch.test(fvalue)){
14922                     cell.title = ddText.replace("%0", fvalue);
14923                     cell.className = " fc-state-disabled";
14924                 }
14925             }
14926             
14927             if (!cell.initialClassName) {
14928                 cell.initialClassName = cell.dom.className;
14929             }
14930             
14931             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14932         };
14933
14934         var i = 0;
14935         
14936         for(; i < startingPos; i++) {
14937             textEls[i].innerHTML = (++prevStart);
14938             d.setDate(d.getDate()+1);
14939             
14940             cells[i].className = "fc-past fc-other-month";
14941             setCellClass(this, cells[i]);
14942         }
14943         
14944         var intDay = 0;
14945         
14946         for(; i < days; i++){
14947             intDay = i - startingPos + 1;
14948             textEls[i].innerHTML = (intDay);
14949             d.setDate(d.getDate()+1);
14950             
14951             cells[i].className = ''; // "x-date-active";
14952             setCellClass(this, cells[i]);
14953         }
14954         var extraDays = 0;
14955         
14956         for(; i < 42; i++) {
14957             textEls[i].innerHTML = (++extraDays);
14958             d.setDate(d.getDate()+1);
14959             
14960             cells[i].className = "fc-future fc-other-month";
14961             setCellClass(this, cells[i]);
14962         }
14963         
14964         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14965         
14966         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14967         
14968         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14969         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14970         
14971         if(totalRows != 6){
14972             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14973             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14974         }
14975         
14976         this.fireEvent('monthchange', this, date);
14977         
14978         
14979         /*
14980         if(!this.internalRender){
14981             var main = this.el.dom.firstChild;
14982             var w = main.offsetWidth;
14983             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14984             Roo.fly(main).setWidth(w);
14985             this.internalRender = true;
14986             // opera does not respect the auto grow header center column
14987             // then, after it gets a width opera refuses to recalculate
14988             // without a second pass
14989             if(Roo.isOpera && !this.secondPass){
14990                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14991                 this.secondPass = true;
14992                 this.update.defer(10, this, [date]);
14993             }
14994         }
14995         */
14996         
14997     },
14998     
14999     findCell : function(dt) {
15000         dt = dt.clearTime().getTime();
15001         var ret = false;
15002         this.cells.each(function(c){
15003             //Roo.log("check " +c.dateValue + '?=' + dt);
15004             if(c.dateValue == dt){
15005                 ret = c;
15006                 return false;
15007             }
15008             return true;
15009         });
15010         
15011         return ret;
15012     },
15013     
15014     findCells : function(ev) {
15015         var s = ev.start.clone().clearTime().getTime();
15016        // Roo.log(s);
15017         var e= ev.end.clone().clearTime().getTime();
15018        // Roo.log(e);
15019         var ret = [];
15020         this.cells.each(function(c){
15021              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15022             
15023             if(c.dateValue > e){
15024                 return ;
15025             }
15026             if(c.dateValue < s){
15027                 return ;
15028             }
15029             ret.push(c);
15030         });
15031         
15032         return ret;    
15033     },
15034     
15035 //    findBestRow: function(cells)
15036 //    {
15037 //        var ret = 0;
15038 //        
15039 //        for (var i =0 ; i < cells.length;i++) {
15040 //            ret  = Math.max(cells[i].rows || 0,ret);
15041 //        }
15042 //        return ret;
15043 //        
15044 //    },
15045     
15046     
15047     addItem : function(ev)
15048     {
15049         // look for vertical location slot in
15050         var cells = this.findCells(ev);
15051         
15052 //        ev.row = this.findBestRow(cells);
15053         
15054         // work out the location.
15055         
15056         var crow = false;
15057         var rows = [];
15058         for(var i =0; i < cells.length; i++) {
15059             
15060             cells[i].row = cells[0].row;
15061             
15062             if(i == 0){
15063                 cells[i].row = cells[i].row + 1;
15064             }
15065             
15066             if (!crow) {
15067                 crow = {
15068                     start : cells[i],
15069                     end :  cells[i]
15070                 };
15071                 continue;
15072             }
15073             if (crow.start.getY() == cells[i].getY()) {
15074                 // on same row.
15075                 crow.end = cells[i];
15076                 continue;
15077             }
15078             // different row.
15079             rows.push(crow);
15080             crow = {
15081                 start: cells[i],
15082                 end : cells[i]
15083             };
15084             
15085         }
15086         
15087         rows.push(crow);
15088         ev.els = [];
15089         ev.rows = rows;
15090         ev.cells = cells;
15091         
15092         cells[0].events.push(ev);
15093         
15094         this.calevents.push(ev);
15095     },
15096     
15097     clearEvents: function() {
15098         
15099         if(!this.calevents){
15100             return;
15101         }
15102         
15103         Roo.each(this.cells.elements, function(c){
15104             c.row = 0;
15105             c.events = [];
15106             c.more = [];
15107         });
15108         
15109         Roo.each(this.calevents, function(e) {
15110             Roo.each(e.els, function(el) {
15111                 el.un('mouseenter' ,this.onEventEnter, this);
15112                 el.un('mouseleave' ,this.onEventLeave, this);
15113                 el.remove();
15114             },this);
15115         },this);
15116         
15117         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15118             e.remove();
15119         });
15120         
15121     },
15122     
15123     renderEvents: function()
15124     {   
15125         var _this = this;
15126         
15127         this.cells.each(function(c) {
15128             
15129             if(c.row < 5){
15130                 return;
15131             }
15132             
15133             var ev = c.events;
15134             
15135             var r = 4;
15136             if(c.row != c.events.length){
15137                 r = 4 - (4 - (c.row - c.events.length));
15138             }
15139             
15140             c.events = ev.slice(0, r);
15141             c.more = ev.slice(r);
15142             
15143             if(c.more.length && c.more.length == 1){
15144                 c.events.push(c.more.pop());
15145             }
15146             
15147             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15148             
15149         });
15150             
15151         this.cells.each(function(c) {
15152             
15153             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15154             
15155             
15156             for (var e = 0; e < c.events.length; e++){
15157                 var ev = c.events[e];
15158                 var rows = ev.rows;
15159                 
15160                 for(var i = 0; i < rows.length; i++) {
15161                 
15162                     // how many rows should it span..
15163
15164                     var  cfg = {
15165                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15166                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15167
15168                         unselectable : "on",
15169                         cn : [
15170                             {
15171                                 cls: 'fc-event-inner',
15172                                 cn : [
15173     //                                {
15174     //                                  tag:'span',
15175     //                                  cls: 'fc-event-time',
15176     //                                  html : cells.length > 1 ? '' : ev.time
15177     //                                },
15178                                     {
15179                                       tag:'span',
15180                                       cls: 'fc-event-title',
15181                                       html : String.format('{0}', ev.title)
15182                                     }
15183
15184
15185                                 ]
15186                             },
15187                             {
15188                                 cls: 'ui-resizable-handle ui-resizable-e',
15189                                 html : '&nbsp;&nbsp;&nbsp'
15190                             }
15191
15192                         ]
15193                     };
15194
15195                     if (i == 0) {
15196                         cfg.cls += ' fc-event-start';
15197                     }
15198                     if ((i+1) == rows.length) {
15199                         cfg.cls += ' fc-event-end';
15200                     }
15201
15202                     var ctr = _this.el.select('.fc-event-container',true).first();
15203                     var cg = ctr.createChild(cfg);
15204
15205                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15206                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15207
15208                     var r = (c.more.length) ? 1 : 0;
15209                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15210                     cg.setWidth(ebox.right - sbox.x -2);
15211
15212                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15213                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15214                     cg.on('click', _this.onEventClick, _this, ev);
15215
15216                     ev.els.push(cg);
15217                     
15218                 }
15219                 
15220             }
15221             
15222             
15223             if(c.more.length){
15224                 var  cfg = {
15225                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15226                     style : 'position: absolute',
15227                     unselectable : "on",
15228                     cn : [
15229                         {
15230                             cls: 'fc-event-inner',
15231                             cn : [
15232                                 {
15233                                   tag:'span',
15234                                   cls: 'fc-event-title',
15235                                   html : 'More'
15236                                 }
15237
15238
15239                             ]
15240                         },
15241                         {
15242                             cls: 'ui-resizable-handle ui-resizable-e',
15243                             html : '&nbsp;&nbsp;&nbsp'
15244                         }
15245
15246                     ]
15247                 };
15248
15249                 var ctr = _this.el.select('.fc-event-container',true).first();
15250                 var cg = ctr.createChild(cfg);
15251
15252                 var sbox = c.select('.fc-day-content',true).first().getBox();
15253                 var ebox = c.select('.fc-day-content',true).first().getBox();
15254                 //Roo.log(cg);
15255                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15256                 cg.setWidth(ebox.right - sbox.x -2);
15257
15258                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15259                 
15260             }
15261             
15262         });
15263         
15264         
15265         
15266     },
15267     
15268     onEventEnter: function (e, el,event,d) {
15269         this.fireEvent('evententer', this, el, event);
15270     },
15271     
15272     onEventLeave: function (e, el,event,d) {
15273         this.fireEvent('eventleave', this, el, event);
15274     },
15275     
15276     onEventClick: function (e, el,event,d) {
15277         this.fireEvent('eventclick', this, el, event);
15278     },
15279     
15280     onMonthChange: function () {
15281         this.store.load();
15282     },
15283     
15284     onMoreEventClick: function(e, el, more)
15285     {
15286         var _this = this;
15287         
15288         this.calpopover.placement = 'right';
15289         this.calpopover.setTitle('More');
15290         
15291         this.calpopover.setContent('');
15292         
15293         var ctr = this.calpopover.el.select('.popover-content', true).first();
15294         
15295         Roo.each(more, function(m){
15296             var cfg = {
15297                 cls : 'fc-event-hori fc-event-draggable',
15298                 html : m.title
15299             }
15300             var cg = ctr.createChild(cfg);
15301             
15302             cg.on('click', _this.onEventClick, _this, m);
15303         });
15304         
15305         this.calpopover.show(el);
15306         
15307         
15308     },
15309     
15310     onLoad: function () 
15311     {   
15312         this.calevents = [];
15313         var cal = this;
15314         
15315         if(this.store.getCount() > 0){
15316             this.store.data.each(function(d){
15317                cal.addItem({
15318                     id : d.data.id,
15319                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15320                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15321                     time : d.data.start_time,
15322                     title : d.data.title,
15323                     description : d.data.description,
15324                     venue : d.data.venue
15325                 });
15326             });
15327         }
15328         
15329         this.renderEvents();
15330         
15331         if(this.calevents.length && this.loadMask){
15332             this.maskEl.hide();
15333         }
15334     },
15335     
15336     onBeforeLoad: function()
15337     {
15338         this.clearEvents();
15339         if(this.loadMask){
15340             this.maskEl.show();
15341         }
15342     }
15343 });
15344
15345  
15346  /*
15347  * - LGPL
15348  *
15349  * element
15350  * 
15351  */
15352
15353 /**
15354  * @class Roo.bootstrap.Popover
15355  * @extends Roo.bootstrap.Component
15356  * Bootstrap Popover class
15357  * @cfg {String} html contents of the popover   (or false to use children..)
15358  * @cfg {String} title of popover (or false to hide)
15359  * @cfg {String} placement how it is placed
15360  * @cfg {String} trigger click || hover (or false to trigger manually)
15361  * @cfg {String} over what (parent or false to trigger manually.)
15362  * @cfg {Number} delay - delay before showing
15363  
15364  * @constructor
15365  * Create a new Popover
15366  * @param {Object} config The config object
15367  */
15368
15369 Roo.bootstrap.Popover = function(config){
15370     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15371 };
15372
15373 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15374     
15375     title: 'Fill in a title',
15376     html: false,
15377     
15378     placement : 'right',
15379     trigger : 'hover', // hover
15380     
15381     delay : 0,
15382     
15383     over: 'parent',
15384     
15385     can_build_overlaid : false,
15386     
15387     getChildContainer : function()
15388     {
15389         return this.el.select('.popover-content',true).first();
15390     },
15391     
15392     getAutoCreate : function(){
15393          Roo.log('make popover?');
15394         var cfg = {
15395            cls : 'popover roo-dynamic',
15396            style: 'display:block',
15397            cn : [
15398                 {
15399                     cls : 'arrow'
15400                 },
15401                 {
15402                     cls : 'popover-inner',
15403                     cn : [
15404                         {
15405                             tag: 'h3',
15406                             cls: 'popover-title',
15407                             html : this.title
15408                         },
15409                         {
15410                             cls : 'popover-content',
15411                             html : this.html
15412                         }
15413                     ]
15414                     
15415                 }
15416            ]
15417         };
15418         
15419         return cfg;
15420     },
15421     setTitle: function(str)
15422     {
15423         this.title = str;
15424         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15425     },
15426     setContent: function(str)
15427     {
15428         this.html = str;
15429         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15430     },
15431     // as it get's added to the bottom of the page.
15432     onRender : function(ct, position)
15433     {
15434         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15435         if(!this.el){
15436             var cfg = Roo.apply({},  this.getAutoCreate());
15437             cfg.id = Roo.id();
15438             
15439             if (this.cls) {
15440                 cfg.cls += ' ' + this.cls;
15441             }
15442             if (this.style) {
15443                 cfg.style = this.style;
15444             }
15445             Roo.log("adding to ")
15446             this.el = Roo.get(document.body).createChild(cfg, position);
15447             Roo.log(this.el);
15448         }
15449         this.initEvents();
15450     },
15451     
15452     initEvents : function()
15453     {
15454         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15455         this.el.enableDisplayMode('block');
15456         this.el.hide();
15457         if (this.over === false) {
15458             return; 
15459         }
15460         if (this.triggers === false) {
15461             return;
15462         }
15463         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15464         var triggers = this.trigger ? this.trigger.split(' ') : [];
15465         Roo.each(triggers, function(trigger) {
15466         
15467             if (trigger == 'click') {
15468                 on_el.on('click', this.toggle, this);
15469             } else if (trigger != 'manual') {
15470                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15471                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15472       
15473                 on_el.on(eventIn  ,this.enter, this);
15474                 on_el.on(eventOut, this.leave, this);
15475             }
15476         }, this);
15477         
15478     },
15479     
15480     
15481     // private
15482     timeout : null,
15483     hoverState : null,
15484     
15485     toggle : function () {
15486         this.hoverState == 'in' ? this.leave() : this.enter();
15487     },
15488     
15489     enter : function () {
15490        
15491     
15492         clearTimeout(this.timeout);
15493     
15494         this.hoverState = 'in';
15495     
15496         if (!this.delay || !this.delay.show) {
15497             this.show();
15498             return;
15499         }
15500         var _t = this;
15501         this.timeout = setTimeout(function () {
15502             if (_t.hoverState == 'in') {
15503                 _t.show();
15504             }
15505         }, this.delay.show)
15506     },
15507     leave : function() {
15508         clearTimeout(this.timeout);
15509     
15510         this.hoverState = 'out';
15511     
15512         if (!this.delay || !this.delay.hide) {
15513             this.hide();
15514             return;
15515         }
15516         var _t = this;
15517         this.timeout = setTimeout(function () {
15518             if (_t.hoverState == 'out') {
15519                 _t.hide();
15520             }
15521         }, this.delay.hide)
15522     },
15523     
15524     show : function (on_el)
15525     {
15526         if (!on_el) {
15527             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15528         }
15529         // set content.
15530         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15531         if (this.html !== false) {
15532             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15533         }
15534         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15535         if (!this.title.length) {
15536             this.el.select('.popover-title',true).hide();
15537         }
15538         
15539         var placement = typeof this.placement == 'function' ?
15540             this.placement.call(this, this.el, on_el) :
15541             this.placement;
15542             
15543         var autoToken = /\s?auto?\s?/i;
15544         var autoPlace = autoToken.test(placement);
15545         if (autoPlace) {
15546             placement = placement.replace(autoToken, '') || 'top';
15547         }
15548         
15549         //this.el.detach()
15550         //this.el.setXY([0,0]);
15551         this.el.show();
15552         this.el.dom.style.display='block';
15553         this.el.addClass(placement);
15554         
15555         //this.el.appendTo(on_el);
15556         
15557         var p = this.getPosition();
15558         var box = this.el.getBox();
15559         
15560         if (autoPlace) {
15561             // fixme..
15562         }
15563         var align = Roo.bootstrap.Popover.alignment[placement];
15564         this.el.alignTo(on_el, align[0],align[1]);
15565         //var arrow = this.el.select('.arrow',true).first();
15566         //arrow.set(align[2], 
15567         
15568         this.el.addClass('in');
15569         
15570         
15571         if (this.el.hasClass('fade')) {
15572             // fade it?
15573         }
15574         
15575     },
15576     hide : function()
15577     {
15578         this.el.setXY([0,0]);
15579         this.el.removeClass('in');
15580         this.el.hide();
15581         this.hoverState = null;
15582         
15583     }
15584     
15585 });
15586
15587 Roo.bootstrap.Popover.alignment = {
15588     'left' : ['r-l', [-10,0], 'right'],
15589     'right' : ['l-r', [10,0], 'left'],
15590     'bottom' : ['t-b', [0,10], 'top'],
15591     'top' : [ 'b-t', [0,-10], 'bottom']
15592 };
15593
15594  /*
15595  * - LGPL
15596  *
15597  * Progress
15598  * 
15599  */
15600
15601 /**
15602  * @class Roo.bootstrap.Progress
15603  * @extends Roo.bootstrap.Component
15604  * Bootstrap Progress class
15605  * @cfg {Boolean} striped striped of the progress bar
15606  * @cfg {Boolean} active animated of the progress bar
15607  * 
15608  * 
15609  * @constructor
15610  * Create a new Progress
15611  * @param {Object} config The config object
15612  */
15613
15614 Roo.bootstrap.Progress = function(config){
15615     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15616 };
15617
15618 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15619     
15620     striped : false,
15621     active: false,
15622     
15623     getAutoCreate : function(){
15624         var cfg = {
15625             tag: 'div',
15626             cls: 'progress'
15627         };
15628         
15629         
15630         if(this.striped){
15631             cfg.cls += ' progress-striped';
15632         }
15633       
15634         if(this.active){
15635             cfg.cls += ' active';
15636         }
15637         
15638         
15639         return cfg;
15640     }
15641    
15642 });
15643
15644  
15645
15646  /*
15647  * - LGPL
15648  *
15649  * ProgressBar
15650  * 
15651  */
15652
15653 /**
15654  * @class Roo.bootstrap.ProgressBar
15655  * @extends Roo.bootstrap.Component
15656  * Bootstrap ProgressBar class
15657  * @cfg {Number} aria_valuenow aria-value now
15658  * @cfg {Number} aria_valuemin aria-value min
15659  * @cfg {Number} aria_valuemax aria-value max
15660  * @cfg {String} label label for the progress bar
15661  * @cfg {String} panel (success | info | warning | danger )
15662  * @cfg {String} role role of the progress bar
15663  * @cfg {String} sr_only text
15664  * 
15665  * 
15666  * @constructor
15667  * Create a new ProgressBar
15668  * @param {Object} config The config object
15669  */
15670
15671 Roo.bootstrap.ProgressBar = function(config){
15672     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15673 };
15674
15675 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15676     
15677     aria_valuenow : 0,
15678     aria_valuemin : 0,
15679     aria_valuemax : 100,
15680     label : false,
15681     panel : false,
15682     role : false,
15683     sr_only: false,
15684     
15685     getAutoCreate : function()
15686     {
15687         
15688         var cfg = {
15689             tag: 'div',
15690             cls: 'progress-bar',
15691             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15692         };
15693         
15694         if(this.sr_only){
15695             cfg.cn = {
15696                 tag: 'span',
15697                 cls: 'sr-only',
15698                 html: this.sr_only
15699             }
15700         }
15701         
15702         if(this.role){
15703             cfg.role = this.role;
15704         }
15705         
15706         if(this.aria_valuenow){
15707             cfg['aria-valuenow'] = this.aria_valuenow;
15708         }
15709         
15710         if(this.aria_valuemin){
15711             cfg['aria-valuemin'] = this.aria_valuemin;
15712         }
15713         
15714         if(this.aria_valuemax){
15715             cfg['aria-valuemax'] = this.aria_valuemax;
15716         }
15717         
15718         if(this.label && !this.sr_only){
15719             cfg.html = this.label;
15720         }
15721         
15722         if(this.panel){
15723             cfg.cls += ' progress-bar-' + this.panel;
15724         }
15725         
15726         return cfg;
15727     },
15728     
15729     update : function(aria_valuenow)
15730     {
15731         this.aria_valuenow = aria_valuenow;
15732         
15733         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15734     }
15735    
15736 });
15737
15738  
15739
15740  /*
15741  * - LGPL
15742  *
15743  * column
15744  * 
15745  */
15746
15747 /**
15748  * @class Roo.bootstrap.TabGroup
15749  * @extends Roo.bootstrap.Column
15750  * Bootstrap Column class
15751  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15752  * @cfg {Boolean} carousel true to make the group behave like a carousel
15753  * @cfg {Number} bullets show the panel pointer.. default 0
15754  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15755  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15756  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15757  * 
15758  * @constructor
15759  * Create a new TabGroup
15760  * @param {Object} config The config object
15761  */
15762
15763 Roo.bootstrap.TabGroup = function(config){
15764     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15765     if (!this.navId) {
15766         this.navId = Roo.id();
15767     }
15768     this.tabs = [];
15769     Roo.bootstrap.TabGroup.register(this);
15770     
15771 };
15772
15773 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15774     
15775     carousel : false,
15776     transition : false,
15777     bullets : 0,
15778     timer : 0,
15779     autoslide : false,
15780     slideFn : false,
15781     slideOnTouch : false,
15782     
15783     getAutoCreate : function()
15784     {
15785         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15786         
15787         cfg.cls += ' tab-content';
15788         
15789         Roo.log('get auto create...............');
15790         
15791         if (this.carousel) {
15792             cfg.cls += ' carousel slide';
15793             
15794             cfg.cn = [{
15795                cls : 'carousel-inner'
15796             }];
15797         
15798             if(this.bullets > 0 && !Roo.isTouch){
15799                 
15800                 var bullets = {
15801                     cls : 'carousel-bullets',
15802                     cn : []
15803                 };
15804                 
15805                 if(this.bullets_cls){
15806                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15807                 }
15808                 
15809                 for (var i = 0; i < this.bullets; i++){
15810                     bullets.cn.push({
15811                         cls : 'bullet bullet-' + i
15812                     });
15813                 }
15814                 
15815                 bullets.cn.push({
15816                     cls : 'clear'
15817                 });
15818                 
15819                 cfg.cn[0].cn = bullets;
15820             }
15821         }
15822         
15823         return cfg;
15824     },
15825     
15826     initEvents:  function()
15827     {
15828         Roo.log('-------- init events on tab group ---------');
15829         
15830         if(this.bullets > 0 && !Roo.isTouch){
15831             this.initBullet();
15832         }
15833         
15834         Roo.log(this);
15835         
15836         if(Roo.isTouch && this.slideOnTouch){
15837             this.el.on("touchstart", this.onTouchStart, this);
15838         }
15839         
15840         if(this.autoslide){
15841             var _this = this;
15842             
15843             this.slideFn = window.setInterval(function() {
15844                 _this.showPanelNext();
15845             }, this.timer);
15846         }
15847         
15848     },
15849     
15850     onTouchStart : function(e, el, o)
15851     {
15852         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15853             return;
15854         }
15855         
15856         this.showPanelNext();
15857     },
15858     
15859     getChildContainer : function()
15860     {
15861         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15862     },
15863     
15864     /**
15865     * register a Navigation item
15866     * @param {Roo.bootstrap.NavItem} the navitem to add
15867     */
15868     register : function(item)
15869     {
15870         this.tabs.push( item);
15871         item.navId = this.navId; // not really needed..
15872     
15873     },
15874     
15875     getActivePanel : function()
15876     {
15877         var r = false;
15878         Roo.each(this.tabs, function(t) {
15879             if (t.active) {
15880                 r = t;
15881                 return false;
15882             }
15883             return null;
15884         });
15885         return r;
15886         
15887     },
15888     getPanelByName : function(n)
15889     {
15890         var r = false;
15891         Roo.each(this.tabs, function(t) {
15892             if (t.tabId == n) {
15893                 r = t;
15894                 return false;
15895             }
15896             return null;
15897         });
15898         return r;
15899     },
15900     indexOfPanel : function(p)
15901     {
15902         var r = false;
15903         Roo.each(this.tabs, function(t,i) {
15904             if (t.tabId == p.tabId) {
15905                 r = i;
15906                 return false;
15907             }
15908             return null;
15909         });
15910         return r;
15911     },
15912     /**
15913      * show a specific panel
15914      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15915      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15916      */
15917     showPanel : function (pan)
15918     {
15919         if(this.transition){
15920             Roo.log("waiting for the transitionend");
15921             return;
15922         }
15923         
15924         if (typeof(pan) == 'number') {
15925             pan = this.tabs[pan];
15926         }
15927         if (typeof(pan) == 'string') {
15928             pan = this.getPanelByName(pan);
15929         }
15930         if (pan.tabId == this.getActivePanel().tabId) {
15931             return true;
15932         }
15933         var cur = this.getActivePanel();
15934         
15935         if (false === cur.fireEvent('beforedeactivate')) {
15936             return false;
15937         }
15938         
15939         if(this.bullets > 0 && !Roo.isTouch){
15940             this.setActiveBullet(this.indexOfPanel(pan));
15941         }
15942         
15943         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15944             
15945             this.transition = true;
15946             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15947             var lr = dir == 'next' ? 'left' : 'right';
15948             pan.el.addClass(dir); // or prev
15949             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15950             cur.el.addClass(lr); // or right
15951             pan.el.addClass(lr);
15952             
15953             var _this = this;
15954             cur.el.on('transitionend', function() {
15955                 Roo.log("trans end?");
15956                 
15957                 pan.el.removeClass([lr,dir]);
15958                 pan.setActive(true);
15959                 
15960                 cur.el.removeClass([lr]);
15961                 cur.setActive(false);
15962                 
15963                 _this.transition = false;
15964                 
15965             }, this, { single:  true } );
15966             
15967             return true;
15968         }
15969         
15970         cur.setActive(false);
15971         pan.setActive(true);
15972         
15973         return true;
15974         
15975     },
15976     showPanelNext : function()
15977     {
15978         var i = this.indexOfPanel(this.getActivePanel());
15979         
15980         if (i >= this.tabs.length - 1 && !this.autoslide) {
15981             return;
15982         }
15983         
15984         if (i >= this.tabs.length - 1 && this.autoslide) {
15985             i = -1;
15986         }
15987         
15988         this.showPanel(this.tabs[i+1]);
15989     },
15990     
15991     showPanelPrev : function()
15992     {
15993         var i = this.indexOfPanel(this.getActivePanel());
15994         
15995         if (i  < 1 && !this.autoslide) {
15996             return;
15997         }
15998         
15999         if (i < 1 && this.autoslide) {
16000             i = this.tabs.length;
16001         }
16002         
16003         this.showPanel(this.tabs[i-1]);
16004     },
16005     
16006     initBullet : function()
16007     {
16008         if(Roo.isTouch){
16009             return;
16010         }
16011         
16012         var _this = this;
16013         
16014         for (var i = 0; i < this.bullets; i++){
16015             var bullet = this.el.select('.bullet-' + i, true).first();
16016
16017             if(!bullet){
16018                 continue;
16019             }
16020
16021             bullet.on('click', (function(e, el, o, ii, t){
16022
16023                 e.preventDefault();
16024
16025                 _this.showPanel(ii);
16026
16027                 if(_this.autoslide && _this.slideFn){
16028                     clearInterval(_this.slideFn);
16029                     _this.slideFn = window.setInterval(function() {
16030                         _this.showPanelNext();
16031                     }, _this.timer);
16032                 }
16033
16034             }).createDelegate(this, [i, bullet], true));
16035         }
16036     },
16037     
16038     setActiveBullet : function(i)
16039     {
16040         if(Roo.isTouch){
16041             return;
16042         }
16043         
16044         Roo.each(this.el.select('.bullet', true).elements, function(el){
16045             el.removeClass('selected');
16046         });
16047
16048         var bullet = this.el.select('.bullet-' + i, true).first();
16049         
16050         if(!bullet){
16051             return;
16052         }
16053         
16054         bullet.addClass('selected');
16055     }
16056     
16057     
16058   
16059 });
16060
16061  
16062
16063  
16064  
16065 Roo.apply(Roo.bootstrap.TabGroup, {
16066     
16067     groups: {},
16068      /**
16069     * register a Navigation Group
16070     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16071     */
16072     register : function(navgrp)
16073     {
16074         this.groups[navgrp.navId] = navgrp;
16075         
16076     },
16077     /**
16078     * fetch a Navigation Group based on the navigation ID
16079     * if one does not exist , it will get created.
16080     * @param {string} the navgroup to add
16081     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16082     */
16083     get: function(navId) {
16084         if (typeof(this.groups[navId]) == 'undefined') {
16085             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16086         }
16087         return this.groups[navId] ;
16088     }
16089     
16090     
16091     
16092 });
16093
16094  /*
16095  * - LGPL
16096  *
16097  * TabPanel
16098  * 
16099  */
16100
16101 /**
16102  * @class Roo.bootstrap.TabPanel
16103  * @extends Roo.bootstrap.Component
16104  * Bootstrap TabPanel class
16105  * @cfg {Boolean} active panel active
16106  * @cfg {String} html panel content
16107  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16108  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16109  * 
16110  * 
16111  * @constructor
16112  * Create a new TabPanel
16113  * @param {Object} config The config object
16114  */
16115
16116 Roo.bootstrap.TabPanel = function(config){
16117     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16118     this.addEvents({
16119         /**
16120              * @event changed
16121              * Fires when the active status changes
16122              * @param {Roo.bootstrap.TabPanel} this
16123              * @param {Boolean} state the new state
16124             
16125          */
16126         'changed': true,
16127         /**
16128              * @event beforedeactivate
16129              * Fires before a tab is de-activated - can be used to do validation on a form.
16130              * @param {Roo.bootstrap.TabPanel} this
16131              * @return {Boolean} false if there is an error
16132             
16133          */
16134         'beforedeactivate': true
16135      });
16136     
16137     this.tabId = this.tabId || Roo.id();
16138   
16139 };
16140
16141 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16142     
16143     active: false,
16144     html: false,
16145     tabId: false,
16146     navId : false,
16147     
16148     getAutoCreate : function(){
16149         var cfg = {
16150             tag: 'div',
16151             // item is needed for carousel - not sure if it has any effect otherwise
16152             cls: 'tab-pane item',
16153             html: this.html || ''
16154         };
16155         
16156         if(this.active){
16157             cfg.cls += ' active';
16158         }
16159         
16160         if(this.tabId){
16161             cfg.tabId = this.tabId;
16162         }
16163         
16164         
16165         return cfg;
16166     },
16167     
16168     initEvents:  function()
16169     {
16170         Roo.log('-------- init events on tab panel ---------');
16171         
16172         var p = this.parent();
16173         this.navId = this.navId || p.navId;
16174         
16175         if (typeof(this.navId) != 'undefined') {
16176             // not really needed.. but just in case.. parent should be a NavGroup.
16177             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16178             Roo.log(['register', tg, this]);
16179             tg.register(this);
16180             
16181             var i = tg.tabs.length - 1;
16182             
16183             if(this.active && tg.bullets > 0 && i < tg.bullets){
16184                 tg.setActiveBullet(i);
16185             }
16186         }
16187         
16188     },
16189     
16190     
16191     onRender : function(ct, position)
16192     {
16193        // Roo.log("Call onRender: " + this.xtype);
16194         
16195         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16196         
16197         
16198         
16199         
16200         
16201     },
16202     
16203     setActive: function(state)
16204     {
16205         Roo.log("panel - set active " + this.tabId + "=" + state);
16206         
16207         this.active = state;
16208         if (!state) {
16209             this.el.removeClass('active');
16210             
16211         } else  if (!this.el.hasClass('active')) {
16212             this.el.addClass('active');
16213         }
16214         
16215         this.fireEvent('changed', this, state);
16216     }
16217     
16218     
16219 });
16220  
16221
16222  
16223
16224  /*
16225  * - LGPL
16226  *
16227  * DateField
16228  * 
16229  */
16230
16231 /**
16232  * @class Roo.bootstrap.DateField
16233  * @extends Roo.bootstrap.Input
16234  * Bootstrap DateField class
16235  * @cfg {Number} weekStart default 0
16236  * @cfg {String} viewMode default empty, (months|years)
16237  * @cfg {String} minViewMode default empty, (months|years)
16238  * @cfg {Number} startDate default -Infinity
16239  * @cfg {Number} endDate default Infinity
16240  * @cfg {Boolean} todayHighlight default false
16241  * @cfg {Boolean} todayBtn default false
16242  * @cfg {Boolean} calendarWeeks default false
16243  * @cfg {Object} daysOfWeekDisabled default empty
16244  * @cfg {Boolean} singleMode default false (true | false)
16245  * 
16246  * @cfg {Boolean} keyboardNavigation default true
16247  * @cfg {String} language default en
16248  * 
16249  * @constructor
16250  * Create a new DateField
16251  * @param {Object} config The config object
16252  */
16253
16254 Roo.bootstrap.DateField = function(config){
16255     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16256      this.addEvents({
16257             /**
16258              * @event show
16259              * Fires when this field show.
16260              * @param {Roo.bootstrap.DateField} this
16261              * @param {Mixed} date The date value
16262              */
16263             show : true,
16264             /**
16265              * @event show
16266              * Fires when this field hide.
16267              * @param {Roo.bootstrap.DateField} this
16268              * @param {Mixed} date The date value
16269              */
16270             hide : true,
16271             /**
16272              * @event select
16273              * Fires when select a date.
16274              * @param {Roo.bootstrap.DateField} this
16275              * @param {Mixed} date The date value
16276              */
16277             select : true
16278         });
16279 };
16280
16281 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16282     
16283     /**
16284      * @cfg {String} format
16285      * The default date format string which can be overriden for localization support.  The format must be
16286      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16287      */
16288     format : "m/d/y",
16289     /**
16290      * @cfg {String} altFormats
16291      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16292      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16293      */
16294     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16295     
16296     weekStart : 0,
16297     
16298     viewMode : '',
16299     
16300     minViewMode : '',
16301     
16302     todayHighlight : false,
16303     
16304     todayBtn: false,
16305     
16306     language: 'en',
16307     
16308     keyboardNavigation: true,
16309     
16310     calendarWeeks: false,
16311     
16312     startDate: -Infinity,
16313     
16314     endDate: Infinity,
16315     
16316     daysOfWeekDisabled: [],
16317     
16318     _events: [],
16319     
16320     singleMode : false,
16321     
16322     UTCDate: function()
16323     {
16324         return new Date(Date.UTC.apply(Date, arguments));
16325     },
16326     
16327     UTCToday: function()
16328     {
16329         var today = new Date();
16330         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16331     },
16332     
16333     getDate: function() {
16334             var d = this.getUTCDate();
16335             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16336     },
16337     
16338     getUTCDate: function() {
16339             return this.date;
16340     },
16341     
16342     setDate: function(d) {
16343             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16344     },
16345     
16346     setUTCDate: function(d) {
16347             this.date = d;
16348             this.setValue(this.formatDate(this.date));
16349     },
16350         
16351     onRender: function(ct, position)
16352     {
16353         
16354         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16355         
16356         this.language = this.language || 'en';
16357         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16358         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16359         
16360         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16361         this.format = this.format || 'm/d/y';
16362         this.isInline = false;
16363         this.isInput = true;
16364         this.component = this.el.select('.add-on', true).first() || false;
16365         this.component = (this.component && this.component.length === 0) ? false : this.component;
16366         this.hasInput = this.component && this.inputEL().length;
16367         
16368         if (typeof(this.minViewMode === 'string')) {
16369             switch (this.minViewMode) {
16370                 case 'months':
16371                     this.minViewMode = 1;
16372                     break;
16373                 case 'years':
16374                     this.minViewMode = 2;
16375                     break;
16376                 default:
16377                     this.minViewMode = 0;
16378                     break;
16379             }
16380         }
16381         
16382         if (typeof(this.viewMode === 'string')) {
16383             switch (this.viewMode) {
16384                 case 'months':
16385                     this.viewMode = 1;
16386                     break;
16387                 case 'years':
16388                     this.viewMode = 2;
16389                     break;
16390                 default:
16391                     this.viewMode = 0;
16392                     break;
16393             }
16394         }
16395                 
16396         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16397         
16398 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16399         
16400         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16401         
16402         this.picker().on('mousedown', this.onMousedown, this);
16403         this.picker().on('click', this.onClick, this);
16404         
16405         this.picker().addClass('datepicker-dropdown');
16406         
16407         this.startViewMode = this.viewMode;
16408         
16409         if(this.singleMode){
16410             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16411                 v.setVisibilityMode(Roo.Element.DISPLAY)
16412                 v.hide();
16413             });
16414             
16415             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16416                 v.setStyle('width', '189px');
16417             });
16418         }
16419         
16420         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16421             if(!this.calendarWeeks){
16422                 v.remove();
16423                 return;
16424             }
16425             
16426             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16427             v.attr('colspan', function(i, val){
16428                 return parseInt(val) + 1;
16429             });
16430         })
16431                         
16432         
16433         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16434         
16435         this.setStartDate(this.startDate);
16436         this.setEndDate(this.endDate);
16437         
16438         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16439         
16440         this.fillDow();
16441         this.fillMonths();
16442         this.update();
16443         this.showMode();
16444         
16445         if(this.isInline) {
16446             this.show();
16447         }
16448     },
16449     
16450     picker : function()
16451     {
16452         return this.pickerEl;
16453 //        return this.el.select('.datepicker', true).first();
16454     },
16455     
16456     fillDow: function()
16457     {
16458         var dowCnt = this.weekStart;
16459         
16460         var dow = {
16461             tag: 'tr',
16462             cn: [
16463                 
16464             ]
16465         };
16466         
16467         if(this.calendarWeeks){
16468             dow.cn.push({
16469                 tag: 'th',
16470                 cls: 'cw',
16471                 html: '&nbsp;'
16472             })
16473         }
16474         
16475         while (dowCnt < this.weekStart + 7) {
16476             dow.cn.push({
16477                 tag: 'th',
16478                 cls: 'dow',
16479                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16480             });
16481         }
16482         
16483         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16484     },
16485     
16486     fillMonths: function()
16487     {    
16488         var i = 0;
16489         var months = this.picker().select('>.datepicker-months td', true).first();
16490         
16491         months.dom.innerHTML = '';
16492         
16493         while (i < 12) {
16494             var month = {
16495                 tag: 'span',
16496                 cls: 'month',
16497                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16498             }
16499             
16500             months.createChild(month);
16501         }
16502         
16503     },
16504     
16505     update: function()
16506     {
16507         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;
16508         
16509         if (this.date < this.startDate) {
16510             this.viewDate = new Date(this.startDate);
16511         } else if (this.date > this.endDate) {
16512             this.viewDate = new Date(this.endDate);
16513         } else {
16514             this.viewDate = new Date(this.date);
16515         }
16516         
16517         this.fill();
16518     },
16519     
16520     fill: function() 
16521     {
16522         var d = new Date(this.viewDate),
16523                 year = d.getUTCFullYear(),
16524                 month = d.getUTCMonth(),
16525                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16526                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16527                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16528                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16529                 currentDate = this.date && this.date.valueOf(),
16530                 today = this.UTCToday();
16531         
16532         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16533         
16534 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16535         
16536 //        this.picker.select('>tfoot th.today').
16537 //                                              .text(dates[this.language].today)
16538 //                                              .toggle(this.todayBtn !== false);
16539     
16540         this.updateNavArrows();
16541         this.fillMonths();
16542                                                 
16543         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16544         
16545         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16546          
16547         prevMonth.setUTCDate(day);
16548         
16549         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16550         
16551         var nextMonth = new Date(prevMonth);
16552         
16553         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16554         
16555         nextMonth = nextMonth.valueOf();
16556         
16557         var fillMonths = false;
16558         
16559         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16560         
16561         while(prevMonth.valueOf() < nextMonth) {
16562             var clsName = '';
16563             
16564             if (prevMonth.getUTCDay() === this.weekStart) {
16565                 if(fillMonths){
16566                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16567                 }
16568                     
16569                 fillMonths = {
16570                     tag: 'tr',
16571                     cn: []
16572                 };
16573                 
16574                 if(this.calendarWeeks){
16575                     // ISO 8601: First week contains first thursday.
16576                     // ISO also states week starts on Monday, but we can be more abstract here.
16577                     var
16578                     // Start of current week: based on weekstart/current date
16579                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16580                     // Thursday of this week
16581                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16582                     // First Thursday of year, year from thursday
16583                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16584                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16585                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16586                     
16587                     fillMonths.cn.push({
16588                         tag: 'td',
16589                         cls: 'cw',
16590                         html: calWeek
16591                     });
16592                 }
16593             }
16594             
16595             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16596                 clsName += ' old';
16597             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16598                 clsName += ' new';
16599             }
16600             if (this.todayHighlight &&
16601                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16602                 prevMonth.getUTCMonth() == today.getMonth() &&
16603                 prevMonth.getUTCDate() == today.getDate()) {
16604                 clsName += ' today';
16605             }
16606             
16607             if (currentDate && prevMonth.valueOf() === currentDate) {
16608                 clsName += ' active';
16609             }
16610             
16611             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16612                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16613                     clsName += ' disabled';
16614             }
16615             
16616             fillMonths.cn.push({
16617                 tag: 'td',
16618                 cls: 'day ' + clsName,
16619                 html: prevMonth.getDate()
16620             })
16621             
16622             prevMonth.setDate(prevMonth.getDate()+1);
16623         }
16624           
16625         var currentYear = this.date && this.date.getUTCFullYear();
16626         var currentMonth = this.date && this.date.getUTCMonth();
16627         
16628         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16629         
16630         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16631             v.removeClass('active');
16632             
16633             if(currentYear === year && k === currentMonth){
16634                 v.addClass('active');
16635             }
16636             
16637             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16638                 v.addClass('disabled');
16639             }
16640             
16641         });
16642         
16643         
16644         year = parseInt(year/10, 10) * 10;
16645         
16646         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16647         
16648         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16649         
16650         year -= 1;
16651         for (var i = -1; i < 11; i++) {
16652             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16653                 tag: 'span',
16654                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16655                 html: year
16656             })
16657             
16658             year += 1;
16659         }
16660     },
16661     
16662     showMode: function(dir) 
16663     {
16664         if (dir) {
16665             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16666         }
16667         
16668         Roo.each(this.picker().select('>div',true).elements, function(v){
16669             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16670             v.hide();
16671         });
16672         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16673     },
16674     
16675     place: function()
16676     {
16677         if(this.isInline) return;
16678         
16679         this.picker().removeClass(['bottom', 'top']);
16680         
16681         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16682             /*
16683              * place to the top of element!
16684              *
16685              */
16686             
16687             this.picker().addClass('top');
16688             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16689             
16690             return;
16691         }
16692         
16693         this.picker().addClass('bottom');
16694         
16695         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16696     },
16697     
16698     parseDate : function(value)
16699     {
16700         if(!value || value instanceof Date){
16701             return value;
16702         }
16703         var v = Date.parseDate(value, this.format);
16704         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16705             v = Date.parseDate(value, 'Y-m-d');
16706         }
16707         if(!v && this.altFormats){
16708             if(!this.altFormatsArray){
16709                 this.altFormatsArray = this.altFormats.split("|");
16710             }
16711             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16712                 v = Date.parseDate(value, this.altFormatsArray[i]);
16713             }
16714         }
16715         return v;
16716     },
16717     
16718     formatDate : function(date, fmt)
16719     {   
16720         return (!date || !(date instanceof Date)) ?
16721         date : date.dateFormat(fmt || this.format);
16722     },
16723     
16724     onFocus : function()
16725     {
16726         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16727         this.show();
16728     },
16729     
16730     onBlur : function()
16731     {
16732         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16733         
16734         var d = this.inputEl().getValue();
16735         
16736         this.setValue(d);
16737                 
16738         this.hide();
16739     },
16740     
16741     show : function()
16742     {
16743         this.picker().show();
16744         this.update();
16745         this.place();
16746         
16747         this.fireEvent('show', this, this.date);
16748     },
16749     
16750     hide : function()
16751     {
16752         if(this.isInline) return;
16753         this.picker().hide();
16754         this.viewMode = this.startViewMode;
16755         this.showMode();
16756         
16757         this.fireEvent('hide', this, this.date);
16758         
16759     },
16760     
16761     onMousedown: function(e)
16762     {
16763         e.stopPropagation();
16764         e.preventDefault();
16765     },
16766     
16767     keyup: function(e)
16768     {
16769         Roo.bootstrap.DateField.superclass.keyup.call(this);
16770         this.update();
16771     },
16772
16773     setValue: function(v)
16774     {
16775         
16776         // v can be a string or a date..
16777         
16778         
16779         var d = new Date(this.parseDate(v) ).clearTime();
16780         
16781         if(isNaN(d.getTime())){
16782             this.date = this.viewDate = '';
16783             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16784             return;
16785         }
16786         
16787         v = this.formatDate(d);
16788         
16789         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16790         
16791         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16792      
16793         this.update();
16794
16795         this.fireEvent('select', this, this.date);
16796         
16797     },
16798     
16799     getValue: function()
16800     {
16801         return this.formatDate(this.date);
16802     },
16803     
16804     fireKey: function(e)
16805     {
16806         if (!this.picker().isVisible()){
16807             if (e.keyCode == 27) // allow escape to hide and re-show picker
16808                 this.show();
16809             return;
16810         }
16811         
16812         var dateChanged = false,
16813         dir, day, month,
16814         newDate, newViewDate;
16815         
16816         switch(e.keyCode){
16817             case 27: // escape
16818                 this.hide();
16819                 e.preventDefault();
16820                 break;
16821             case 37: // left
16822             case 39: // right
16823                 if (!this.keyboardNavigation) break;
16824                 dir = e.keyCode == 37 ? -1 : 1;
16825                 
16826                 if (e.ctrlKey){
16827                     newDate = this.moveYear(this.date, dir);
16828                     newViewDate = this.moveYear(this.viewDate, dir);
16829                 } else if (e.shiftKey){
16830                     newDate = this.moveMonth(this.date, dir);
16831                     newViewDate = this.moveMonth(this.viewDate, dir);
16832                 } else {
16833                     newDate = new Date(this.date);
16834                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16835                     newViewDate = new Date(this.viewDate);
16836                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16837                 }
16838                 if (this.dateWithinRange(newDate)){
16839                     this.date = newDate;
16840                     this.viewDate = newViewDate;
16841                     this.setValue(this.formatDate(this.date));
16842 //                    this.update();
16843                     e.preventDefault();
16844                     dateChanged = true;
16845                 }
16846                 break;
16847             case 38: // up
16848             case 40: // down
16849                 if (!this.keyboardNavigation) break;
16850                 dir = e.keyCode == 38 ? -1 : 1;
16851                 if (e.ctrlKey){
16852                     newDate = this.moveYear(this.date, dir);
16853                     newViewDate = this.moveYear(this.viewDate, dir);
16854                 } else if (e.shiftKey){
16855                     newDate = this.moveMonth(this.date, dir);
16856                     newViewDate = this.moveMonth(this.viewDate, dir);
16857                 } else {
16858                     newDate = new Date(this.date);
16859                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16860                     newViewDate = new Date(this.viewDate);
16861                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16862                 }
16863                 if (this.dateWithinRange(newDate)){
16864                     this.date = newDate;
16865                     this.viewDate = newViewDate;
16866                     this.setValue(this.formatDate(this.date));
16867 //                    this.update();
16868                     e.preventDefault();
16869                     dateChanged = true;
16870                 }
16871                 break;
16872             case 13: // enter
16873                 this.setValue(this.formatDate(this.date));
16874                 this.hide();
16875                 e.preventDefault();
16876                 break;
16877             case 9: // tab
16878                 this.setValue(this.formatDate(this.date));
16879                 this.hide();
16880                 break;
16881             case 16: // shift
16882             case 17: // ctrl
16883             case 18: // alt
16884                 break;
16885             default :
16886                 this.hide();
16887                 
16888         }
16889     },
16890     
16891     
16892     onClick: function(e) 
16893     {
16894         e.stopPropagation();
16895         e.preventDefault();
16896         
16897         var target = e.getTarget();
16898         
16899         if(target.nodeName.toLowerCase() === 'i'){
16900             target = Roo.get(target).dom.parentNode;
16901         }
16902         
16903         var nodeName = target.nodeName;
16904         var className = target.className;
16905         var html = target.innerHTML;
16906         //Roo.log(nodeName);
16907         
16908         switch(nodeName.toLowerCase()) {
16909             case 'th':
16910                 switch(className) {
16911                     case 'switch':
16912                         this.showMode(1);
16913                         break;
16914                     case 'prev':
16915                     case 'next':
16916                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16917                         switch(this.viewMode){
16918                                 case 0:
16919                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16920                                         break;
16921                                 case 1:
16922                                 case 2:
16923                                         this.viewDate = this.moveYear(this.viewDate, dir);
16924                                         break;
16925                         }
16926                         this.fill();
16927                         break;
16928                     case 'today':
16929                         var date = new Date();
16930                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16931 //                        this.fill()
16932                         this.setValue(this.formatDate(this.date));
16933                         
16934                         this.hide();
16935                         break;
16936                 }
16937                 break;
16938             case 'span':
16939                 if (className.indexOf('disabled') < 0) {
16940                     this.viewDate.setUTCDate(1);
16941                     if (className.indexOf('month') > -1) {
16942                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16943                     } else {
16944                         var year = parseInt(html, 10) || 0;
16945                         this.viewDate.setUTCFullYear(year);
16946                         
16947                     }
16948                     
16949                     if(this.singleMode){
16950                         this.setValue(this.formatDate(this.viewDate));
16951                         this.hide();
16952                         return;
16953                     }
16954                     
16955                     this.showMode(-1);
16956                     this.fill();
16957                 }
16958                 break;
16959                 
16960             case 'td':
16961                 //Roo.log(className);
16962                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16963                     var day = parseInt(html, 10) || 1;
16964                     var year = this.viewDate.getUTCFullYear(),
16965                         month = this.viewDate.getUTCMonth();
16966
16967                     if (className.indexOf('old') > -1) {
16968                         if(month === 0 ){
16969                             month = 11;
16970                             year -= 1;
16971                         }else{
16972                             month -= 1;
16973                         }
16974                     } else if (className.indexOf('new') > -1) {
16975                         if (month == 11) {
16976                             month = 0;
16977                             year += 1;
16978                         } else {
16979                             month += 1;
16980                         }
16981                     }
16982                     //Roo.log([year,month,day]);
16983                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16984                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16985 //                    this.fill();
16986                     //Roo.log(this.formatDate(this.date));
16987                     this.setValue(this.formatDate(this.date));
16988                     this.hide();
16989                 }
16990                 break;
16991         }
16992     },
16993     
16994     setStartDate: function(startDate)
16995     {
16996         this.startDate = startDate || -Infinity;
16997         if (this.startDate !== -Infinity) {
16998             this.startDate = this.parseDate(this.startDate);
16999         }
17000         this.update();
17001         this.updateNavArrows();
17002     },
17003
17004     setEndDate: function(endDate)
17005     {
17006         this.endDate = endDate || Infinity;
17007         if (this.endDate !== Infinity) {
17008             this.endDate = this.parseDate(this.endDate);
17009         }
17010         this.update();
17011         this.updateNavArrows();
17012     },
17013     
17014     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17015     {
17016         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17017         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17018             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17019         }
17020         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17021             return parseInt(d, 10);
17022         });
17023         this.update();
17024         this.updateNavArrows();
17025     },
17026     
17027     updateNavArrows: function() 
17028     {
17029         if(this.singleMode){
17030             return;
17031         }
17032         
17033         var d = new Date(this.viewDate),
17034         year = d.getUTCFullYear(),
17035         month = d.getUTCMonth();
17036         
17037         Roo.each(this.picker().select('.prev', true).elements, function(v){
17038             v.show();
17039             switch (this.viewMode) {
17040                 case 0:
17041
17042                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17043                         v.hide();
17044                     }
17045                     break;
17046                 case 1:
17047                 case 2:
17048                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17049                         v.hide();
17050                     }
17051                     break;
17052             }
17053         });
17054         
17055         Roo.each(this.picker().select('.next', true).elements, function(v){
17056             v.show();
17057             switch (this.viewMode) {
17058                 case 0:
17059
17060                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17061                         v.hide();
17062                     }
17063                     break;
17064                 case 1:
17065                 case 2:
17066                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17067                         v.hide();
17068                     }
17069                     break;
17070             }
17071         })
17072     },
17073     
17074     moveMonth: function(date, dir)
17075     {
17076         if (!dir) return date;
17077         var new_date = new Date(date.valueOf()),
17078         day = new_date.getUTCDate(),
17079         month = new_date.getUTCMonth(),
17080         mag = Math.abs(dir),
17081         new_month, test;
17082         dir = dir > 0 ? 1 : -1;
17083         if (mag == 1){
17084             test = dir == -1
17085             // If going back one month, make sure month is not current month
17086             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17087             ? function(){
17088                 return new_date.getUTCMonth() == month;
17089             }
17090             // If going forward one month, make sure month is as expected
17091             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17092             : function(){
17093                 return new_date.getUTCMonth() != new_month;
17094             };
17095             new_month = month + dir;
17096             new_date.setUTCMonth(new_month);
17097             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17098             if (new_month < 0 || new_month > 11)
17099                 new_month = (new_month + 12) % 12;
17100         } else {
17101             // For magnitudes >1, move one month at a time...
17102             for (var i=0; i<mag; i++)
17103                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17104                 new_date = this.moveMonth(new_date, dir);
17105             // ...then reset the day, keeping it in the new month
17106             new_month = new_date.getUTCMonth();
17107             new_date.setUTCDate(day);
17108             test = function(){
17109                 return new_month != new_date.getUTCMonth();
17110             };
17111         }
17112         // Common date-resetting loop -- if date is beyond end of month, make it
17113         // end of month
17114         while (test()){
17115             new_date.setUTCDate(--day);
17116             new_date.setUTCMonth(new_month);
17117         }
17118         return new_date;
17119     },
17120
17121     moveYear: function(date, dir)
17122     {
17123         return this.moveMonth(date, dir*12);
17124     },
17125
17126     dateWithinRange: function(date)
17127     {
17128         return date >= this.startDate && date <= this.endDate;
17129     },
17130
17131     
17132     remove: function() 
17133     {
17134         this.picker().remove();
17135     }
17136    
17137 });
17138
17139 Roo.apply(Roo.bootstrap.DateField,  {
17140     
17141     head : {
17142         tag: 'thead',
17143         cn: [
17144         {
17145             tag: 'tr',
17146             cn: [
17147             {
17148                 tag: 'th',
17149                 cls: 'prev',
17150                 html: '<i class="fa fa-arrow-left"/>'
17151             },
17152             {
17153                 tag: 'th',
17154                 cls: 'switch',
17155                 colspan: '5'
17156             },
17157             {
17158                 tag: 'th',
17159                 cls: 'next',
17160                 html: '<i class="fa fa-arrow-right"/>'
17161             }
17162
17163             ]
17164         }
17165         ]
17166     },
17167     
17168     content : {
17169         tag: 'tbody',
17170         cn: [
17171         {
17172             tag: 'tr',
17173             cn: [
17174             {
17175                 tag: 'td',
17176                 colspan: '7'
17177             }
17178             ]
17179         }
17180         ]
17181     },
17182     
17183     footer : {
17184         tag: 'tfoot',
17185         cn: [
17186         {
17187             tag: 'tr',
17188             cn: [
17189             {
17190                 tag: 'th',
17191                 colspan: '7',
17192                 cls: 'today'
17193             }
17194                     
17195             ]
17196         }
17197         ]
17198     },
17199     
17200     dates:{
17201         en: {
17202             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17203             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17204             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17205             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17206             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17207             today: "Today"
17208         }
17209     },
17210     
17211     modes: [
17212     {
17213         clsName: 'days',
17214         navFnc: 'Month',
17215         navStep: 1
17216     },
17217     {
17218         clsName: 'months',
17219         navFnc: 'FullYear',
17220         navStep: 1
17221     },
17222     {
17223         clsName: 'years',
17224         navFnc: 'FullYear',
17225         navStep: 10
17226     }]
17227 });
17228
17229 Roo.apply(Roo.bootstrap.DateField,  {
17230   
17231     template : {
17232         tag: 'div',
17233         cls: 'datepicker dropdown-menu roo-dynamic',
17234         cn: [
17235         {
17236             tag: 'div',
17237             cls: 'datepicker-days',
17238             cn: [
17239             {
17240                 tag: 'table',
17241                 cls: 'table-condensed',
17242                 cn:[
17243                 Roo.bootstrap.DateField.head,
17244                 {
17245                     tag: 'tbody'
17246                 },
17247                 Roo.bootstrap.DateField.footer
17248                 ]
17249             }
17250             ]
17251         },
17252         {
17253             tag: 'div',
17254             cls: 'datepicker-months',
17255             cn: [
17256             {
17257                 tag: 'table',
17258                 cls: 'table-condensed',
17259                 cn:[
17260                 Roo.bootstrap.DateField.head,
17261                 Roo.bootstrap.DateField.content,
17262                 Roo.bootstrap.DateField.footer
17263                 ]
17264             }
17265             ]
17266         },
17267         {
17268             tag: 'div',
17269             cls: 'datepicker-years',
17270             cn: [
17271             {
17272                 tag: 'table',
17273                 cls: 'table-condensed',
17274                 cn:[
17275                 Roo.bootstrap.DateField.head,
17276                 Roo.bootstrap.DateField.content,
17277                 Roo.bootstrap.DateField.footer
17278                 ]
17279             }
17280             ]
17281         }
17282         ]
17283     }
17284 });
17285
17286  
17287
17288  /*
17289  * - LGPL
17290  *
17291  * TimeField
17292  * 
17293  */
17294
17295 /**
17296  * @class Roo.bootstrap.TimeField
17297  * @extends Roo.bootstrap.Input
17298  * Bootstrap DateField class
17299  * 
17300  * 
17301  * @constructor
17302  * Create a new TimeField
17303  * @param {Object} config The config object
17304  */
17305
17306 Roo.bootstrap.TimeField = function(config){
17307     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17308     this.addEvents({
17309             /**
17310              * @event show
17311              * Fires when this field show.
17312              * @param {Roo.bootstrap.DateField} thisthis
17313              * @param {Mixed} date The date value
17314              */
17315             show : true,
17316             /**
17317              * @event show
17318              * Fires when this field hide.
17319              * @param {Roo.bootstrap.DateField} this
17320              * @param {Mixed} date The date value
17321              */
17322             hide : true,
17323             /**
17324              * @event select
17325              * Fires when select a date.
17326              * @param {Roo.bootstrap.DateField} this
17327              * @param {Mixed} date The date value
17328              */
17329             select : true
17330         });
17331 };
17332
17333 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17334     
17335     /**
17336      * @cfg {String} format
17337      * The default time format string which can be overriden for localization support.  The format must be
17338      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17339      */
17340     format : "H:i",
17341        
17342     onRender: function(ct, position)
17343     {
17344         
17345         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17346                 
17347         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17348         
17349         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17350         
17351         this.pop = this.picker().select('>.datepicker-time',true).first();
17352         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17353         
17354         this.picker().on('mousedown', this.onMousedown, this);
17355         this.picker().on('click', this.onClick, this);
17356         
17357         this.picker().addClass('datepicker-dropdown');
17358     
17359         this.fillTime();
17360         this.update();
17361             
17362         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17363         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17364         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17365         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17366         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17367         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17368
17369     },
17370     
17371     fireKey: function(e){
17372         if (!this.picker().isVisible()){
17373             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17374                 this.show();
17375             }
17376             return;
17377         }
17378
17379         e.preventDefault();
17380         
17381         switch(e.keyCode){
17382             case 27: // escape
17383                 this.hide();
17384                 break;
17385             case 37: // left
17386             case 39: // right
17387                 this.onTogglePeriod();
17388                 break;
17389             case 38: // up
17390                 this.onIncrementMinutes();
17391                 break;
17392             case 40: // down
17393                 this.onDecrementMinutes();
17394                 break;
17395             case 13: // enter
17396             case 9: // tab
17397                 this.setTime();
17398                 break;
17399         }
17400     },
17401     
17402     onClick: function(e) {
17403         e.stopPropagation();
17404         e.preventDefault();
17405     },
17406     
17407     picker : function()
17408     {
17409         return this.el.select('.datepicker', true).first();
17410     },
17411     
17412     fillTime: function()
17413     {    
17414         var time = this.pop.select('tbody', true).first();
17415         
17416         time.dom.innerHTML = '';
17417         
17418         time.createChild({
17419             tag: 'tr',
17420             cn: [
17421                 {
17422                     tag: 'td',
17423                     cn: [
17424                         {
17425                             tag: 'a',
17426                             href: '#',
17427                             cls: 'btn',
17428                             cn: [
17429                                 {
17430                                     tag: 'span',
17431                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17432                                 }
17433                             ]
17434                         } 
17435                     ]
17436                 },
17437                 {
17438                     tag: 'td',
17439                     cls: 'separator'
17440                 },
17441                 {
17442                     tag: 'td',
17443                     cn: [
17444                         {
17445                             tag: 'a',
17446                             href: '#',
17447                             cls: 'btn',
17448                             cn: [
17449                                 {
17450                                     tag: 'span',
17451                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17452                                 }
17453                             ]
17454                         }
17455                     ]
17456                 },
17457                 {
17458                     tag: 'td',
17459                     cls: 'separator'
17460                 }
17461             ]
17462         });
17463         
17464         time.createChild({
17465             tag: 'tr',
17466             cn: [
17467                 {
17468                     tag: 'td',
17469                     cn: [
17470                         {
17471                             tag: 'span',
17472                             cls: 'timepicker-hour',
17473                             html: '00'
17474                         }  
17475                     ]
17476                 },
17477                 {
17478                     tag: 'td',
17479                     cls: 'separator',
17480                     html: ':'
17481                 },
17482                 {
17483                     tag: 'td',
17484                     cn: [
17485                         {
17486                             tag: 'span',
17487                             cls: 'timepicker-minute',
17488                             html: '00'
17489                         }  
17490                     ]
17491                 },
17492                 {
17493                     tag: 'td',
17494                     cls: 'separator'
17495                 },
17496                 {
17497                     tag: 'td',
17498                     cn: [
17499                         {
17500                             tag: 'button',
17501                             type: 'button',
17502                             cls: 'btn btn-primary period',
17503                             html: 'AM'
17504                             
17505                         }
17506                     ]
17507                 }
17508             ]
17509         });
17510         
17511         time.createChild({
17512             tag: 'tr',
17513             cn: [
17514                 {
17515                     tag: 'td',
17516                     cn: [
17517                         {
17518                             tag: 'a',
17519                             href: '#',
17520                             cls: 'btn',
17521                             cn: [
17522                                 {
17523                                     tag: 'span',
17524                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17525                                 }
17526                             ]
17527                         }
17528                     ]
17529                 },
17530                 {
17531                     tag: 'td',
17532                     cls: 'separator'
17533                 },
17534                 {
17535                     tag: 'td',
17536                     cn: [
17537                         {
17538                             tag: 'a',
17539                             href: '#',
17540                             cls: 'btn',
17541                             cn: [
17542                                 {
17543                                     tag: 'span',
17544                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17545                                 }
17546                             ]
17547                         }
17548                     ]
17549                 },
17550                 {
17551                     tag: 'td',
17552                     cls: 'separator'
17553                 }
17554             ]
17555         });
17556         
17557     },
17558     
17559     update: function()
17560     {
17561         
17562         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17563         
17564         this.fill();
17565     },
17566     
17567     fill: function() 
17568     {
17569         var hours = this.time.getHours();
17570         var minutes = this.time.getMinutes();
17571         var period = 'AM';
17572         
17573         if(hours > 11){
17574             period = 'PM';
17575         }
17576         
17577         if(hours == 0){
17578             hours = 12;
17579         }
17580         
17581         
17582         if(hours > 12){
17583             hours = hours - 12;
17584         }
17585         
17586         if(hours < 10){
17587             hours = '0' + hours;
17588         }
17589         
17590         if(minutes < 10){
17591             minutes = '0' + minutes;
17592         }
17593         
17594         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17595         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17596         this.pop.select('button', true).first().dom.innerHTML = period;
17597         
17598     },
17599     
17600     place: function()
17601     {   
17602         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17603         
17604         var cls = ['bottom'];
17605         
17606         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17607             cls.pop();
17608             cls.push('top');
17609         }
17610         
17611         cls.push('right');
17612         
17613         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17614             cls.pop();
17615             cls.push('left');
17616         }
17617         
17618         this.picker().addClass(cls.join('-'));
17619         
17620         var _this = this;
17621         
17622         Roo.each(cls, function(c){
17623             if(c == 'bottom'){
17624                 _this.picker().setTop(_this.inputEl().getHeight());
17625                 return;
17626             }
17627             if(c == 'top'){
17628                 _this.picker().setTop(0 - _this.picker().getHeight());
17629                 return;
17630             }
17631             
17632             if(c == 'left'){
17633                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17634                 return;
17635             }
17636             if(c == 'right'){
17637                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17638                 return;
17639             }
17640         });
17641         
17642     },
17643   
17644     onFocus : function()
17645     {
17646         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17647         this.show();
17648     },
17649     
17650     onBlur : function()
17651     {
17652         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17653         this.hide();
17654     },
17655     
17656     show : function()
17657     {
17658         this.picker().show();
17659         this.pop.show();
17660         this.update();
17661         this.place();
17662         
17663         this.fireEvent('show', this, this.date);
17664     },
17665     
17666     hide : function()
17667     {
17668         this.picker().hide();
17669         this.pop.hide();
17670         
17671         this.fireEvent('hide', this, this.date);
17672     },
17673     
17674     setTime : function()
17675     {
17676         this.hide();
17677         this.setValue(this.time.format(this.format));
17678         
17679         this.fireEvent('select', this, this.date);
17680         
17681         
17682     },
17683     
17684     onMousedown: function(e){
17685         e.stopPropagation();
17686         e.preventDefault();
17687     },
17688     
17689     onIncrementHours: function()
17690     {
17691         Roo.log('onIncrementHours');
17692         this.time = this.time.add(Date.HOUR, 1);
17693         this.update();
17694         
17695     },
17696     
17697     onDecrementHours: function()
17698     {
17699         Roo.log('onDecrementHours');
17700         this.time = this.time.add(Date.HOUR, -1);
17701         this.update();
17702     },
17703     
17704     onIncrementMinutes: function()
17705     {
17706         Roo.log('onIncrementMinutes');
17707         this.time = this.time.add(Date.MINUTE, 1);
17708         this.update();
17709     },
17710     
17711     onDecrementMinutes: function()
17712     {
17713         Roo.log('onDecrementMinutes');
17714         this.time = this.time.add(Date.MINUTE, -1);
17715         this.update();
17716     },
17717     
17718     onTogglePeriod: function()
17719     {
17720         Roo.log('onTogglePeriod');
17721         this.time = this.time.add(Date.HOUR, 12);
17722         this.update();
17723     }
17724     
17725    
17726 });
17727
17728 Roo.apply(Roo.bootstrap.TimeField,  {
17729     
17730     content : {
17731         tag: 'tbody',
17732         cn: [
17733             {
17734                 tag: 'tr',
17735                 cn: [
17736                 {
17737                     tag: 'td',
17738                     colspan: '7'
17739                 }
17740                 ]
17741             }
17742         ]
17743     },
17744     
17745     footer : {
17746         tag: 'tfoot',
17747         cn: [
17748             {
17749                 tag: 'tr',
17750                 cn: [
17751                 {
17752                     tag: 'th',
17753                     colspan: '7',
17754                     cls: '',
17755                     cn: [
17756                         {
17757                             tag: 'button',
17758                             cls: 'btn btn-info ok',
17759                             html: 'OK'
17760                         }
17761                     ]
17762                 }
17763
17764                 ]
17765             }
17766         ]
17767     }
17768 });
17769
17770 Roo.apply(Roo.bootstrap.TimeField,  {
17771   
17772     template : {
17773         tag: 'div',
17774         cls: 'datepicker dropdown-menu',
17775         cn: [
17776             {
17777                 tag: 'div',
17778                 cls: 'datepicker-time',
17779                 cn: [
17780                 {
17781                     tag: 'table',
17782                     cls: 'table-condensed',
17783                     cn:[
17784                     Roo.bootstrap.TimeField.content,
17785                     Roo.bootstrap.TimeField.footer
17786                     ]
17787                 }
17788                 ]
17789             }
17790         ]
17791     }
17792 });
17793
17794  
17795
17796  /*
17797  * - LGPL
17798  *
17799  * MonthField
17800  * 
17801  */
17802
17803 /**
17804  * @class Roo.bootstrap.MonthField
17805  * @extends Roo.bootstrap.Input
17806  * Bootstrap MonthField class
17807  * 
17808  * @cfg {String} language default en
17809  * 
17810  * @constructor
17811  * Create a new MonthField
17812  * @param {Object} config The config object
17813  */
17814
17815 Roo.bootstrap.MonthField = function(config){
17816     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17817     
17818     this.addEvents({
17819         /**
17820          * @event show
17821          * Fires when this field show.
17822          * @param {Roo.bootstrap.MonthField} this
17823          * @param {Mixed} date The date value
17824          */
17825         show : true,
17826         /**
17827          * @event show
17828          * Fires when this field hide.
17829          * @param {Roo.bootstrap.MonthField} this
17830          * @param {Mixed} date The date value
17831          */
17832         hide : true,
17833         /**
17834          * @event select
17835          * Fires when select a date.
17836          * @param {Roo.bootstrap.MonthField} this
17837          * @param {String} oldvalue The old value
17838          * @param {String} newvalue The new value
17839          */
17840         select : true
17841     });
17842 };
17843
17844 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17845     
17846     onRender: function(ct, position)
17847     {
17848         
17849         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17850         
17851         this.language = this.language || 'en';
17852         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17853         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17854         
17855         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17856         this.isInline = false;
17857         this.isInput = true;
17858         this.component = this.el.select('.add-on', true).first() || false;
17859         this.component = (this.component && this.component.length === 0) ? false : this.component;
17860         this.hasInput = this.component && this.inputEL().length;
17861         
17862         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17863         
17864         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17865         
17866         this.picker().on('mousedown', this.onMousedown, this);
17867         this.picker().on('click', this.onClick, this);
17868         
17869         this.picker().addClass('datepicker-dropdown');
17870         
17871         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17872             v.setStyle('width', '189px');
17873         });
17874         
17875         this.fillMonths();
17876         
17877         this.update();
17878         
17879         if(this.isInline) {
17880             this.show();
17881         }
17882         
17883     },
17884     
17885     setValue: function(v, suppressEvent)
17886     {   
17887         var o = this.getValue();
17888         
17889         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17890         
17891         this.update();
17892
17893         if(suppressEvent !== true){
17894             this.fireEvent('select', this, o, v);
17895         }
17896         
17897     },
17898     
17899     getValue: function()
17900     {
17901         return this.value;
17902     },
17903     
17904     onClick: function(e) 
17905     {
17906         e.stopPropagation();
17907         e.preventDefault();
17908         
17909         var target = e.getTarget();
17910         
17911         if(target.nodeName.toLowerCase() === 'i'){
17912             target = Roo.get(target).dom.parentNode;
17913         }
17914         
17915         var nodeName = target.nodeName;
17916         var className = target.className;
17917         var html = target.innerHTML;
17918         
17919         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17920             return;
17921         }
17922         
17923         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17924         
17925         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17926         
17927         this.hide();
17928                         
17929     },
17930     
17931     picker : function()
17932     {
17933         return this.pickerEl;
17934     },
17935     
17936     fillMonths: function()
17937     {    
17938         var i = 0;
17939         var months = this.picker().select('>.datepicker-months td', true).first();
17940         
17941         months.dom.innerHTML = '';
17942         
17943         while (i < 12) {
17944             var month = {
17945                 tag: 'span',
17946                 cls: 'month',
17947                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17948             }
17949             
17950             months.createChild(month);
17951         }
17952         
17953     },
17954     
17955     update: function()
17956     {
17957         var _this = this;
17958         
17959         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17960             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17961         }
17962         
17963         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17964             e.removeClass('active');
17965             
17966             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17967                 e.addClass('active');
17968             }
17969         })
17970     },
17971     
17972     place: function()
17973     {
17974         if(this.isInline) return;
17975         
17976         this.picker().removeClass(['bottom', 'top']);
17977         
17978         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17979             /*
17980              * place to the top of element!
17981              *
17982              */
17983             
17984             this.picker().addClass('top');
17985             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17986             
17987             return;
17988         }
17989         
17990         this.picker().addClass('bottom');
17991         
17992         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17993     },
17994     
17995     onFocus : function()
17996     {
17997         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17998         this.show();
17999     },
18000     
18001     onBlur : function()
18002     {
18003         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18004         
18005         var d = this.inputEl().getValue();
18006         
18007         this.setValue(d);
18008                 
18009         this.hide();
18010     },
18011     
18012     show : function()
18013     {
18014         this.picker().show();
18015         this.picker().select('>.datepicker-months', true).first().show();
18016         this.update();
18017         this.place();
18018         
18019         this.fireEvent('show', this, this.date);
18020     },
18021     
18022     hide : function()
18023     {
18024         if(this.isInline) return;
18025         this.picker().hide();
18026         this.fireEvent('hide', this, this.date);
18027         
18028     },
18029     
18030     onMousedown: function(e)
18031     {
18032         e.stopPropagation();
18033         e.preventDefault();
18034     },
18035     
18036     keyup: function(e)
18037     {
18038         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18039         this.update();
18040     },
18041
18042     fireKey: function(e)
18043     {
18044         if (!this.picker().isVisible()){
18045             if (e.keyCode == 27) // allow escape to hide and re-show picker
18046                 this.show();
18047             return;
18048         }
18049         
18050         var dir;
18051         
18052         switch(e.keyCode){
18053             case 27: // escape
18054                 this.hide();
18055                 e.preventDefault();
18056                 break;
18057             case 37: // left
18058             case 39: // right
18059                 dir = e.keyCode == 37 ? -1 : 1;
18060                 
18061                 this.vIndex = this.vIndex + dir;
18062                 
18063                 if(this.vIndex < 0){
18064                     this.vIndex = 0;
18065                 }
18066                 
18067                 if(this.vIndex > 11){
18068                     this.vIndex = 11;
18069                 }
18070                 
18071                 if(isNaN(this.vIndex)){
18072                     this.vIndex = 0;
18073                 }
18074                 
18075                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18076                 
18077                 break;
18078             case 38: // up
18079             case 40: // down
18080                 
18081                 dir = e.keyCode == 38 ? -1 : 1;
18082                 
18083                 this.vIndex = this.vIndex + dir * 4;
18084                 
18085                 if(this.vIndex < 0){
18086                     this.vIndex = 0;
18087                 }
18088                 
18089                 if(this.vIndex > 11){
18090                     this.vIndex = 11;
18091                 }
18092                 
18093                 if(isNaN(this.vIndex)){
18094                     this.vIndex = 0;
18095                 }
18096                 
18097                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18098                 break;
18099                 
18100             case 13: // enter
18101                 
18102                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18103                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18104                 }
18105                 
18106                 this.hide();
18107                 e.preventDefault();
18108                 break;
18109             case 9: // tab
18110                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18111                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18112                 }
18113                 this.hide();
18114                 break;
18115             case 16: // shift
18116             case 17: // ctrl
18117             case 18: // alt
18118                 break;
18119             default :
18120                 this.hide();
18121                 
18122         }
18123     },
18124     
18125     remove: function() 
18126     {
18127         this.picker().remove();
18128     }
18129    
18130 });
18131
18132 Roo.apply(Roo.bootstrap.MonthField,  {
18133     
18134     content : {
18135         tag: 'tbody',
18136         cn: [
18137         {
18138             tag: 'tr',
18139             cn: [
18140             {
18141                 tag: 'td',
18142                 colspan: '7'
18143             }
18144             ]
18145         }
18146         ]
18147     },
18148     
18149     dates:{
18150         en: {
18151             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18152             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18153         }
18154     }
18155 });
18156
18157 Roo.apply(Roo.bootstrap.MonthField,  {
18158   
18159     template : {
18160         tag: 'div',
18161         cls: 'datepicker dropdown-menu roo-dynamic',
18162         cn: [
18163             {
18164                 tag: 'div',
18165                 cls: 'datepicker-months',
18166                 cn: [
18167                 {
18168                     tag: 'table',
18169                     cls: 'table-condensed',
18170                     cn:[
18171                         Roo.bootstrap.DateField.content
18172                     ]
18173                 }
18174                 ]
18175             }
18176         ]
18177     }
18178 });
18179
18180  
18181
18182  
18183  /*
18184  * - LGPL
18185  *
18186  * CheckBox
18187  * 
18188  */
18189
18190 /**
18191  * @class Roo.bootstrap.CheckBox
18192  * @extends Roo.bootstrap.Input
18193  * Bootstrap CheckBox class
18194  * 
18195  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18196  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18197  * @cfg {String} boxLabel The text that appears beside the checkbox
18198  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18199  * @cfg {Boolean} checked initnal the element
18200  * @cfg {Boolean} inline inline the element (default false)
18201  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18202  * 
18203  * @constructor
18204  * Create a new CheckBox
18205  * @param {Object} config The config object
18206  */
18207
18208 Roo.bootstrap.CheckBox = function(config){
18209     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18210    
18211     this.addEvents({
18212         /**
18213         * @event check
18214         * Fires when the element is checked or unchecked.
18215         * @param {Roo.bootstrap.CheckBox} this This input
18216         * @param {Boolean} checked The new checked value
18217         */
18218        check : true
18219     });
18220     
18221 };
18222
18223 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18224   
18225     inputType: 'checkbox',
18226     inputValue: 1,
18227     valueOff: 0,
18228     boxLabel: false,
18229     checked: false,
18230     weight : false,
18231     inline: false,
18232     
18233     getAutoCreate : function()
18234     {
18235         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18236         
18237         var id = Roo.id();
18238         
18239         var cfg = {};
18240         
18241         cfg.cls = 'form-group ' + this.inputType; //input-group
18242         
18243         if(this.inline){
18244             cfg.cls += ' ' + this.inputType + '-inline';
18245         }
18246         
18247         var input =  {
18248             tag: 'input',
18249             id : id,
18250             type : this.inputType,
18251             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18252             cls : 'roo-' + this.inputType, //'form-box',
18253             placeholder : this.placeholder || ''
18254             
18255         };
18256         
18257         if (this.weight) { // Validity check?
18258             cfg.cls += " " + this.inputType + "-" + this.weight;
18259         }
18260         
18261         if (this.disabled) {
18262             input.disabled=true;
18263         }
18264         
18265         if(this.checked){
18266             input.checked = this.checked;
18267         }
18268         
18269         if (this.name) {
18270             input.name = this.name;
18271         }
18272         
18273         if (this.size) {
18274             input.cls += ' input-' + this.size;
18275         }
18276         
18277         var settings=this;
18278         
18279         ['xs','sm','md','lg'].map(function(size){
18280             if (settings[size]) {
18281                 cfg.cls += ' col-' + size + '-' + settings[size];
18282             }
18283         });
18284         
18285         var inputblock = input;
18286          
18287         if (this.before || this.after) {
18288             
18289             inputblock = {
18290                 cls : 'input-group',
18291                 cn :  [] 
18292             };
18293             
18294             if (this.before) {
18295                 inputblock.cn.push({
18296                     tag :'span',
18297                     cls : 'input-group-addon',
18298                     html : this.before
18299                 });
18300             }
18301             
18302             inputblock.cn.push(input);
18303             
18304             if (this.after) {
18305                 inputblock.cn.push({
18306                     tag :'span',
18307                     cls : 'input-group-addon',
18308                     html : this.after
18309                 });
18310             }
18311             
18312         }
18313         
18314         if (align ==='left' && this.fieldLabel.length) {
18315                 Roo.log("left and has label");
18316                 cfg.cn = [
18317                     
18318                     {
18319                         tag: 'label',
18320                         'for' :  id,
18321                         cls : 'control-label col-md-' + this.labelWidth,
18322                         html : this.fieldLabel
18323                         
18324                     },
18325                     {
18326                         cls : "col-md-" + (12 - this.labelWidth), 
18327                         cn: [
18328                             inputblock
18329                         ]
18330                     }
18331                     
18332                 ];
18333         } else if ( this.fieldLabel.length) {
18334                 Roo.log(" label");
18335                 cfg.cn = [
18336                    
18337                     {
18338                         tag: this.boxLabel ? 'span' : 'label',
18339                         'for': id,
18340                         cls: 'control-label box-input-label',
18341                         //cls : 'input-group-addon',
18342                         html : this.fieldLabel
18343                         
18344                     },
18345                     
18346                     inputblock
18347                     
18348                 ];
18349
18350         } else {
18351             
18352                 Roo.log(" no label && no align");
18353                 cfg.cn = [  inputblock ] ;
18354                 
18355                 
18356         }
18357         if(this.boxLabel){
18358              var boxLabelCfg = {
18359                 tag: 'label',
18360                 //'for': id, // box label is handled by onclick - so no for...
18361                 cls: 'box-label',
18362                 html: this.boxLabel
18363             }
18364             
18365             if(this.tooltip){
18366                 boxLabelCfg.tooltip = this.tooltip;
18367             }
18368              
18369             cfg.cn.push(boxLabelCfg);
18370         }
18371         
18372         
18373        
18374         return cfg;
18375         
18376     },
18377     
18378     /**
18379      * return the real input element.
18380      */
18381     inputEl: function ()
18382     {
18383         return this.el.select('input.roo-' + this.inputType,true).first();
18384     },
18385     
18386     labelEl: function()
18387     {
18388         return this.el.select('label.control-label',true).first();
18389     },
18390     /* depricated... */
18391     
18392     label: function()
18393     {
18394         return this.labelEl();
18395     },
18396     
18397     initEvents : function()
18398     {
18399 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18400         
18401         this.inputEl().on('click', this.onClick,  this);
18402         
18403         if (this.boxLabel) { 
18404             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18405         }
18406         
18407         this.startValue = this.getValue();
18408         
18409         if(this.groupId){
18410             Roo.bootstrap.CheckBox.register(this);
18411         }
18412     },
18413     
18414     onClick : function()
18415     {   
18416         this.setChecked(!this.checked);
18417     },
18418     
18419     setChecked : function(state,suppressEvent)
18420     {
18421         this.startValue = this.getValue();
18422         
18423         if(this.inputType == 'radio'){
18424             
18425             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18426                 e.dom.checked = false;
18427             });
18428             
18429             this.inputEl().dom.checked = true;
18430             
18431             this.inputEl().dom.value = this.inputValue;
18432             
18433             if(suppressEvent !== true){
18434                 this.fireEvent('check', this, true);
18435             }
18436             
18437             this.validate();
18438             
18439             return;
18440         }
18441         
18442         this.checked = state;
18443         
18444         this.inputEl().dom.checked = state;
18445         
18446         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18447         
18448         if(suppressEvent !== true){
18449             this.fireEvent('check', this, state);
18450         }
18451         
18452         this.validate();
18453     },
18454     
18455     getValue : function()
18456     {
18457         if(this.inputType == 'radio'){
18458             return this.getGroupValue();
18459         }
18460         
18461         return this.inputEl().getValue();
18462         
18463     },
18464     
18465     getGroupValue : function()
18466     {
18467         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18468             return '';
18469         }
18470         
18471         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18472     },
18473     
18474     setValue : function(v,suppressEvent)
18475     {
18476         if(this.inputType == 'radio'){
18477             this.setGroupValue(v, suppressEvent);
18478             return;
18479         }
18480         
18481         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18482         
18483         this.validate();
18484     },
18485     
18486     setGroupValue : function(v, suppressEvent)
18487     {
18488         this.startValue = this.getValue();
18489         
18490         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18491             e.dom.checked = false;
18492             
18493             if(e.dom.value == v){
18494                 e.dom.checked = true;
18495             }
18496         });
18497         
18498         if(suppressEvent !== true){
18499             this.fireEvent('check', this, true);
18500         }
18501
18502         this.validate();
18503         
18504         return;
18505     },
18506     
18507     validate : function()
18508     {
18509         if(
18510                 this.disabled || 
18511                 (this.inputType == 'radio' && this.validateRadio()) ||
18512                 (this.inputType == 'checkbox' && this.validateCheckbox())
18513         ){
18514             this.markValid();
18515             return true;
18516         }
18517         
18518         this.markInvalid();
18519         return false;
18520     },
18521     
18522     validateRadio : function()
18523     {
18524         var valid = false;
18525         
18526         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18527             if(!e.dom.checked){
18528                 return;
18529             }
18530             
18531             valid = true;
18532             
18533             return false;
18534         });
18535         
18536         return valid;
18537     },
18538     
18539     validateCheckbox : function()
18540     {
18541         if(!this.groupId){
18542             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18543         }
18544         
18545         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18546         
18547         if(!group){
18548             return false;
18549         }
18550         
18551         var r = false;
18552         
18553         for(var i in group){
18554             if(r){
18555                 break;
18556             }
18557             
18558             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18559         }
18560         
18561         return r;
18562     },
18563     
18564     /**
18565      * Mark this field as valid
18566      */
18567     markValid : function()
18568     {
18569         if(this.allowBlank){
18570             return;
18571         }
18572         
18573         var _this = this;
18574         
18575         this.fireEvent('valid', this);
18576         
18577         if(this.inputType == 'radio'){
18578             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18579                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18580                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18581             });
18582             
18583             return;
18584         }
18585         
18586         if(!this.groupId){
18587             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18588             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18589             return;
18590         }
18591         
18592         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18593             
18594         if(!group){
18595             return;
18596         }
18597         
18598         for(var i in group){
18599             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18600             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18601         }
18602     },
18603     
18604      /**
18605      * Mark this field as invalid
18606      * @param {String} msg The validation message
18607      */
18608     markInvalid : function(msg)
18609     {
18610         if(this.allowBlank){
18611             return;
18612         }
18613         
18614         var _this = this;
18615         
18616         this.fireEvent('invalid', this, msg);
18617         
18618         if(this.inputType == 'radio'){
18619             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18620                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18621                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18622             });
18623             
18624             return;
18625         }
18626         
18627         if(!this.groupId){
18628             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18629             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18630             return;
18631         }
18632         
18633         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18634             
18635         if(!group){
18636             return;
18637         }
18638         
18639         for(var i in group){
18640             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18641             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18642         }
18643         
18644     }
18645     
18646 });
18647
18648 Roo.apply(Roo.bootstrap.CheckBox, {
18649     
18650     groups: {},
18651     
18652      /**
18653     * register a CheckBox Group
18654     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18655     */
18656     register : function(checkbox)
18657     {
18658         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18659             this.groups[checkbox.groupId] = {};
18660         }
18661         
18662         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18663             return;
18664         }
18665         
18666         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18667         
18668     },
18669     /**
18670     * fetch a CheckBox Group based on the group ID
18671     * @param {string} the group ID
18672     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18673     */
18674     get: function(groupId) {
18675         if (typeof(this.groups[groupId]) == 'undefined') {
18676             return false;
18677         }
18678         
18679         return this.groups[groupId] ;
18680     }
18681     
18682     
18683 });
18684 /*
18685  * - LGPL
18686  *
18687  * Radio
18688  *
18689  *
18690  * not inline
18691  *<div class="radio">
18692   <label>
18693     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18694     Option one is this and that&mdash;be sure to include why it's great
18695   </label>
18696 </div>
18697  *
18698  *
18699  *inline
18700  *<span>
18701  *<label class="radio-inline">fieldLabel</label>
18702  *<label class="radio-inline">
18703   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18704 </label>
18705 <span>
18706  * 
18707  * 
18708  */
18709
18710 /**
18711  * @class Roo.bootstrap.Radio
18712  * @extends Roo.bootstrap.CheckBox
18713  * Bootstrap Radio class
18714
18715  * @constructor
18716  * Create a new Radio
18717  * @param {Object} config The config object
18718  */
18719
18720 Roo.bootstrap.Radio = function(config){
18721     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18722    
18723 };
18724
18725 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18726     
18727     inputType: 'radio',
18728     inputValue: '',
18729     valueOff: '',
18730     
18731     getAutoCreate : function()
18732     {
18733         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18734         align = align || 'left'; // default...
18735         
18736         
18737         
18738         var id = Roo.id();
18739         
18740         var cfg = {
18741                 tag : this.inline ? 'span' : 'div',
18742                 cls : '',
18743                 cn : []
18744         };
18745         
18746         var inline = this.inline ? ' radio-inline' : '';
18747         
18748         var lbl = {
18749                 tag: 'label' ,
18750                 // does not need for, as we wrap the input with it..
18751                 'for' : id,
18752                 cls : 'control-label box-label' + inline,
18753                 cn : []
18754         };
18755         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18756         
18757         var fieldLabel = {
18758             tag: 'label' ,
18759             //cls : 'control-label' + inline,
18760             html : this.fieldLabel,
18761             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18762         };
18763         
18764  
18765         
18766         
18767         var input =  {
18768             tag: 'input',
18769             id : id,
18770             type : this.inputType,
18771             //value : (!this.checked) ? this.valueOff : this.inputValue,
18772             value : this.inputValue,
18773             cls : 'roo-radio',
18774             placeholder : this.placeholder || '' // ?? needed????
18775             
18776         };
18777         if (this.weight) { // Validity check?
18778             input.cls += " radio-" + this.weight;
18779         }
18780         if (this.disabled) {
18781             input.disabled=true;
18782         }
18783         
18784         if(this.checked){
18785             input.checked = this.checked;
18786         }
18787         
18788         if (this.name) {
18789             input.name = this.name;
18790         }
18791         
18792         if (this.size) {
18793             input.cls += ' input-' + this.size;
18794         }
18795         
18796         //?? can span's inline have a width??
18797         
18798         var settings=this;
18799         ['xs','sm','md','lg'].map(function(size){
18800             if (settings[size]) {
18801                 cfg.cls += ' col-' + size + '-' + settings[size];
18802             }
18803         });
18804         
18805         var inputblock = input;
18806         
18807         if (this.before || this.after) {
18808             
18809             inputblock = {
18810                 cls : 'input-group',
18811                 tag : 'span',
18812                 cn :  [] 
18813             };
18814             if (this.before) {
18815                 inputblock.cn.push({
18816                     tag :'span',
18817                     cls : 'input-group-addon',
18818                     html : this.before
18819                 });
18820             }
18821             inputblock.cn.push(input);
18822             if (this.after) {
18823                 inputblock.cn.push({
18824                     tag :'span',
18825                     cls : 'input-group-addon',
18826                     html : this.after
18827                 });
18828             }
18829             
18830         };
18831         
18832         
18833         if (this.fieldLabel && this.fieldLabel.length) {
18834             cfg.cn.push(fieldLabel);
18835         }
18836        
18837         // normal bootstrap puts the input inside the label.
18838         // however with our styled version - it has to go after the input.
18839        
18840         //lbl.cn.push(inputblock);
18841         
18842         var lblwrap =  {
18843             tag: 'span',
18844             cls: 'radio' + inline,
18845             cn: [
18846                 inputblock,
18847                 lbl
18848             ]
18849         };
18850         
18851         cfg.cn.push( lblwrap);
18852         
18853         if(this.boxLabel){
18854             lbl.cn.push({
18855                 tag: 'span',
18856                 html: this.boxLabel
18857             })
18858         }
18859          
18860         
18861         return cfg;
18862         
18863     },
18864     
18865     initEvents : function()
18866     {
18867 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18868         
18869         this.inputEl().on('click', this.onClick,  this);
18870         if (this.boxLabel) {
18871             Roo.log('find label')
18872             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18873         }
18874         
18875     },
18876     
18877     inputEl: function ()
18878     {
18879         return this.el.select('input.roo-radio',true).first();
18880     },
18881     onClick : function()
18882     {   
18883         Roo.log("click");
18884         this.setChecked(true);
18885     },
18886     
18887     setChecked : function(state,suppressEvent)
18888     {
18889         if(state){
18890             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18891                 v.dom.checked = false;
18892             });
18893         }
18894         Roo.log(this.inputEl().dom);
18895         this.checked = state;
18896         this.inputEl().dom.checked = state;
18897         
18898         if(suppressEvent !== true){
18899             this.fireEvent('check', this, state);
18900         }
18901         
18902         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18903         
18904     },
18905     
18906     getGroupValue : function()
18907     {
18908         var value = '';
18909         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18910             if(v.dom.checked == true){
18911                 value = v.dom.value;
18912             }
18913         });
18914         
18915         return value;
18916     },
18917     
18918     /**
18919      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18920      * @return {Mixed} value The field value
18921      */
18922     getValue : function(){
18923         return this.getGroupValue();
18924     }
18925     
18926 });
18927
18928  
18929 //<script type="text/javascript">
18930
18931 /*
18932  * Based  Ext JS Library 1.1.1
18933  * Copyright(c) 2006-2007, Ext JS, LLC.
18934  * LGPL
18935  *
18936  */
18937  
18938 /**
18939  * @class Roo.HtmlEditorCore
18940  * @extends Roo.Component
18941  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18942  *
18943  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18944  */
18945
18946 Roo.HtmlEditorCore = function(config){
18947     
18948     
18949     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18950     
18951     
18952     this.addEvents({
18953         /**
18954          * @event initialize
18955          * Fires when the editor is fully initialized (including the iframe)
18956          * @param {Roo.HtmlEditorCore} this
18957          */
18958         initialize: true,
18959         /**
18960          * @event activate
18961          * Fires when the editor is first receives the focus. Any insertion must wait
18962          * until after this event.
18963          * @param {Roo.HtmlEditorCore} this
18964          */
18965         activate: true,
18966          /**
18967          * @event beforesync
18968          * Fires before the textarea is updated with content from the editor iframe. Return false
18969          * to cancel the sync.
18970          * @param {Roo.HtmlEditorCore} this
18971          * @param {String} html
18972          */
18973         beforesync: true,
18974          /**
18975          * @event beforepush
18976          * Fires before the iframe editor is updated with content from the textarea. Return false
18977          * to cancel the push.
18978          * @param {Roo.HtmlEditorCore} this
18979          * @param {String} html
18980          */
18981         beforepush: true,
18982          /**
18983          * @event sync
18984          * Fires when the textarea is updated with content from the editor iframe.
18985          * @param {Roo.HtmlEditorCore} this
18986          * @param {String} html
18987          */
18988         sync: true,
18989          /**
18990          * @event push
18991          * Fires when the iframe editor is updated with content from the textarea.
18992          * @param {Roo.HtmlEditorCore} this
18993          * @param {String} html
18994          */
18995         push: true,
18996         
18997         /**
18998          * @event editorevent
18999          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19000          * @param {Roo.HtmlEditorCore} this
19001          */
19002         editorevent: true
19003         
19004     });
19005     
19006     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19007     
19008     // defaults : white / black...
19009     this.applyBlacklists();
19010     
19011     
19012     
19013 };
19014
19015
19016 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19017
19018
19019      /**
19020      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19021      */
19022     
19023     owner : false,
19024     
19025      /**
19026      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19027      *                        Roo.resizable.
19028      */
19029     resizable : false,
19030      /**
19031      * @cfg {Number} height (in pixels)
19032      */   
19033     height: 300,
19034    /**
19035      * @cfg {Number} width (in pixels)
19036      */   
19037     width: 500,
19038     
19039     /**
19040      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19041      * 
19042      */
19043     stylesheets: false,
19044     
19045     // id of frame..
19046     frameId: false,
19047     
19048     // private properties
19049     validationEvent : false,
19050     deferHeight: true,
19051     initialized : false,
19052     activated : false,
19053     sourceEditMode : false,
19054     onFocus : Roo.emptyFn,
19055     iframePad:3,
19056     hideMode:'offsets',
19057     
19058     clearUp: true,
19059     
19060     // blacklist + whitelisted elements..
19061     black: false,
19062     white: false,
19063      
19064     
19065
19066     /**
19067      * Protected method that will not generally be called directly. It
19068      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19069      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19070      */
19071     getDocMarkup : function(){
19072         // body styles..
19073         var st = '';
19074         
19075         // inherit styels from page...?? 
19076         if (this.stylesheets === false) {
19077             
19078             Roo.get(document.head).select('style').each(function(node) {
19079                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19080             });
19081             
19082             Roo.get(document.head).select('link').each(function(node) { 
19083                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19084             });
19085             
19086         } else if (!this.stylesheets.length) {
19087                 // simple..
19088                 st = '<style type="text/css">' +
19089                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19090                    '</style>';
19091         } else { 
19092             
19093         }
19094         
19095         st +=  '<style type="text/css">' +
19096             'IMG { cursor: pointer } ' +
19097         '</style>';
19098
19099         
19100         return '<html><head>' + st  +
19101             //<style type="text/css">' +
19102             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19103             //'</style>' +
19104             ' </head><body class="roo-htmleditor-body"></body></html>';
19105     },
19106
19107     // private
19108     onRender : function(ct, position)
19109     {
19110         var _t = this;
19111         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19112         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19113         
19114         
19115         this.el.dom.style.border = '0 none';
19116         this.el.dom.setAttribute('tabIndex', -1);
19117         this.el.addClass('x-hidden hide');
19118         
19119         
19120         
19121         if(Roo.isIE){ // fix IE 1px bogus margin
19122             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19123         }
19124        
19125         
19126         this.frameId = Roo.id();
19127         
19128          
19129         
19130         var iframe = this.owner.wrap.createChild({
19131             tag: 'iframe',
19132             cls: 'form-control', // bootstrap..
19133             id: this.frameId,
19134             name: this.frameId,
19135             frameBorder : 'no',
19136             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19137         }, this.el
19138         );
19139         
19140         
19141         this.iframe = iframe.dom;
19142
19143          this.assignDocWin();
19144         
19145         this.doc.designMode = 'on';
19146        
19147         this.doc.open();
19148         this.doc.write(this.getDocMarkup());
19149         this.doc.close();
19150
19151         
19152         var task = { // must defer to wait for browser to be ready
19153             run : function(){
19154                 //console.log("run task?" + this.doc.readyState);
19155                 this.assignDocWin();
19156                 if(this.doc.body || this.doc.readyState == 'complete'){
19157                     try {
19158                         this.doc.designMode="on";
19159                     } catch (e) {
19160                         return;
19161                     }
19162                     Roo.TaskMgr.stop(task);
19163                     this.initEditor.defer(10, this);
19164                 }
19165             },
19166             interval : 10,
19167             duration: 10000,
19168             scope: this
19169         };
19170         Roo.TaskMgr.start(task);
19171
19172     },
19173
19174     // private
19175     onResize : function(w, h)
19176     {
19177          Roo.log('resize: ' +w + ',' + h );
19178         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19179         if(!this.iframe){
19180             return;
19181         }
19182         if(typeof w == 'number'){
19183             
19184             this.iframe.style.width = w + 'px';
19185         }
19186         if(typeof h == 'number'){
19187             
19188             this.iframe.style.height = h + 'px';
19189             if(this.doc){
19190                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19191             }
19192         }
19193         
19194     },
19195
19196     /**
19197      * Toggles the editor between standard and source edit mode.
19198      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19199      */
19200     toggleSourceEdit : function(sourceEditMode){
19201         
19202         this.sourceEditMode = sourceEditMode === true;
19203         
19204         if(this.sourceEditMode){
19205  
19206             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19207             
19208         }else{
19209             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19210             //this.iframe.className = '';
19211             this.deferFocus();
19212         }
19213         //this.setSize(this.owner.wrap.getSize());
19214         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19215     },
19216
19217     
19218   
19219
19220     /**
19221      * Protected method that will not generally be called directly. If you need/want
19222      * custom HTML cleanup, this is the method you should override.
19223      * @param {String} html The HTML to be cleaned
19224      * return {String} The cleaned HTML
19225      */
19226     cleanHtml : function(html){
19227         html = String(html);
19228         if(html.length > 5){
19229             if(Roo.isSafari){ // strip safari nonsense
19230                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19231             }
19232         }
19233         if(html == '&nbsp;'){
19234             html = '';
19235         }
19236         return html;
19237     },
19238
19239     /**
19240      * HTML Editor -> Textarea
19241      * Protected method that will not generally be called directly. Syncs the contents
19242      * of the editor iframe with the textarea.
19243      */
19244     syncValue : function(){
19245         if(this.initialized){
19246             var bd = (this.doc.body || this.doc.documentElement);
19247             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19248             var html = bd.innerHTML;
19249             if(Roo.isSafari){
19250                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19251                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19252                 if(m && m[1]){
19253                     html = '<div style="'+m[0]+'">' + html + '</div>';
19254                 }
19255             }
19256             html = this.cleanHtml(html);
19257             // fix up the special chars.. normaly like back quotes in word...
19258             // however we do not want to do this with chinese..
19259             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19260                 var cc = b.charCodeAt();
19261                 if (
19262                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19263                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19264                     (cc >= 0xf900 && cc < 0xfb00 )
19265                 ) {
19266                         return b;
19267                 }
19268                 return "&#"+cc+";" 
19269             });
19270             if(this.owner.fireEvent('beforesync', this, html) !== false){
19271                 this.el.dom.value = html;
19272                 this.owner.fireEvent('sync', this, html);
19273             }
19274         }
19275     },
19276
19277     /**
19278      * Protected method that will not generally be called directly. Pushes the value of the textarea
19279      * into the iframe editor.
19280      */
19281     pushValue : function(){
19282         if(this.initialized){
19283             var v = this.el.dom.value.trim();
19284             
19285 //            if(v.length < 1){
19286 //                v = '&#160;';
19287 //            }
19288             
19289             if(this.owner.fireEvent('beforepush', this, v) !== false){
19290                 var d = (this.doc.body || this.doc.documentElement);
19291                 d.innerHTML = v;
19292                 this.cleanUpPaste();
19293                 this.el.dom.value = d.innerHTML;
19294                 this.owner.fireEvent('push', this, v);
19295             }
19296         }
19297     },
19298
19299     // private
19300     deferFocus : function(){
19301         this.focus.defer(10, this);
19302     },
19303
19304     // doc'ed in Field
19305     focus : function(){
19306         if(this.win && !this.sourceEditMode){
19307             this.win.focus();
19308         }else{
19309             this.el.focus();
19310         }
19311     },
19312     
19313     assignDocWin: function()
19314     {
19315         var iframe = this.iframe;
19316         
19317          if(Roo.isIE){
19318             this.doc = iframe.contentWindow.document;
19319             this.win = iframe.contentWindow;
19320         } else {
19321 //            if (!Roo.get(this.frameId)) {
19322 //                return;
19323 //            }
19324 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19325 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19326             
19327             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19328                 return;
19329             }
19330             
19331             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19332             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19333         }
19334     },
19335     
19336     // private
19337     initEditor : function(){
19338         //console.log("INIT EDITOR");
19339         this.assignDocWin();
19340         
19341         
19342         
19343         this.doc.designMode="on";
19344         this.doc.open();
19345         this.doc.write(this.getDocMarkup());
19346         this.doc.close();
19347         
19348         var dbody = (this.doc.body || this.doc.documentElement);
19349         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19350         // this copies styles from the containing element into thsi one..
19351         // not sure why we need all of this..
19352         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19353         
19354         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19355         //ss['background-attachment'] = 'fixed'; // w3c
19356         dbody.bgProperties = 'fixed'; // ie
19357         //Roo.DomHelper.applyStyles(dbody, ss);
19358         Roo.EventManager.on(this.doc, {
19359             //'mousedown': this.onEditorEvent,
19360             'mouseup': this.onEditorEvent,
19361             'dblclick': this.onEditorEvent,
19362             'click': this.onEditorEvent,
19363             'keyup': this.onEditorEvent,
19364             buffer:100,
19365             scope: this
19366         });
19367         if(Roo.isGecko){
19368             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19369         }
19370         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19371             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19372         }
19373         this.initialized = true;
19374
19375         this.owner.fireEvent('initialize', this);
19376         this.pushValue();
19377     },
19378
19379     // private
19380     onDestroy : function(){
19381         
19382         
19383         
19384         if(this.rendered){
19385             
19386             //for (var i =0; i < this.toolbars.length;i++) {
19387             //    // fixme - ask toolbars for heights?
19388             //    this.toolbars[i].onDestroy();
19389            // }
19390             
19391             //this.wrap.dom.innerHTML = '';
19392             //this.wrap.remove();
19393         }
19394     },
19395
19396     // private
19397     onFirstFocus : function(){
19398         
19399         this.assignDocWin();
19400         
19401         
19402         this.activated = true;
19403          
19404     
19405         if(Roo.isGecko){ // prevent silly gecko errors
19406             this.win.focus();
19407             var s = this.win.getSelection();
19408             if(!s.focusNode || s.focusNode.nodeType != 3){
19409                 var r = s.getRangeAt(0);
19410                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19411                 r.collapse(true);
19412                 this.deferFocus();
19413             }
19414             try{
19415                 this.execCmd('useCSS', true);
19416                 this.execCmd('styleWithCSS', false);
19417             }catch(e){}
19418         }
19419         this.owner.fireEvent('activate', this);
19420     },
19421
19422     // private
19423     adjustFont: function(btn){
19424         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19425         //if(Roo.isSafari){ // safari
19426         //    adjust *= 2;
19427        // }
19428         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19429         if(Roo.isSafari){ // safari
19430             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19431             v =  (v < 10) ? 10 : v;
19432             v =  (v > 48) ? 48 : v;
19433             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19434             
19435         }
19436         
19437         
19438         v = Math.max(1, v+adjust);
19439         
19440         this.execCmd('FontSize', v  );
19441     },
19442
19443     onEditorEvent : function(e)
19444     {
19445         this.owner.fireEvent('editorevent', this, e);
19446       //  this.updateToolbar();
19447         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19448     },
19449
19450     insertTag : function(tg)
19451     {
19452         // could be a bit smarter... -> wrap the current selected tRoo..
19453         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19454             
19455             range = this.createRange(this.getSelection());
19456             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19457             wrappingNode.appendChild(range.extractContents());
19458             range.insertNode(wrappingNode);
19459
19460             return;
19461             
19462             
19463             
19464         }
19465         this.execCmd("formatblock",   tg);
19466         
19467     },
19468     
19469     insertText : function(txt)
19470     {
19471         
19472         
19473         var range = this.createRange();
19474         range.deleteContents();
19475                //alert(Sender.getAttribute('label'));
19476                
19477         range.insertNode(this.doc.createTextNode(txt));
19478     } ,
19479     
19480      
19481
19482     /**
19483      * Executes a Midas editor command on the editor document and performs necessary focus and
19484      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19485      * @param {String} cmd The Midas command
19486      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19487      */
19488     relayCmd : function(cmd, value){
19489         this.win.focus();
19490         this.execCmd(cmd, value);
19491         this.owner.fireEvent('editorevent', this);
19492         //this.updateToolbar();
19493         this.owner.deferFocus();
19494     },
19495
19496     /**
19497      * Executes a Midas editor command directly on the editor document.
19498      * For visual commands, you should use {@link #relayCmd} instead.
19499      * <b>This should only be called after the editor is initialized.</b>
19500      * @param {String} cmd The Midas command
19501      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19502      */
19503     execCmd : function(cmd, value){
19504         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19505         this.syncValue();
19506     },
19507  
19508  
19509    
19510     /**
19511      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19512      * to insert tRoo.
19513      * @param {String} text | dom node.. 
19514      */
19515     insertAtCursor : function(text)
19516     {
19517         
19518         
19519         
19520         if(!this.activated){
19521             return;
19522         }
19523         /*
19524         if(Roo.isIE){
19525             this.win.focus();
19526             var r = this.doc.selection.createRange();
19527             if(r){
19528                 r.collapse(true);
19529                 r.pasteHTML(text);
19530                 this.syncValue();
19531                 this.deferFocus();
19532             
19533             }
19534             return;
19535         }
19536         */
19537         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19538             this.win.focus();
19539             
19540             
19541             // from jquery ui (MIT licenced)
19542             var range, node;
19543             var win = this.win;
19544             
19545             if (win.getSelection && win.getSelection().getRangeAt) {
19546                 range = win.getSelection().getRangeAt(0);
19547                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19548                 range.insertNode(node);
19549             } else if (win.document.selection && win.document.selection.createRange) {
19550                 // no firefox support
19551                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19552                 win.document.selection.createRange().pasteHTML(txt);
19553             } else {
19554                 // no firefox support
19555                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19556                 this.execCmd('InsertHTML', txt);
19557             } 
19558             
19559             this.syncValue();
19560             
19561             this.deferFocus();
19562         }
19563     },
19564  // private
19565     mozKeyPress : function(e){
19566         if(e.ctrlKey){
19567             var c = e.getCharCode(), cmd;
19568           
19569             if(c > 0){
19570                 c = String.fromCharCode(c).toLowerCase();
19571                 switch(c){
19572                     case 'b':
19573                         cmd = 'bold';
19574                         break;
19575                     case 'i':
19576                         cmd = 'italic';
19577                         break;
19578                     
19579                     case 'u':
19580                         cmd = 'underline';
19581                         break;
19582                     
19583                     case 'v':
19584                         this.cleanUpPaste.defer(100, this);
19585                         return;
19586                         
19587                 }
19588                 if(cmd){
19589                     this.win.focus();
19590                     this.execCmd(cmd);
19591                     this.deferFocus();
19592                     e.preventDefault();
19593                 }
19594                 
19595             }
19596         }
19597     },
19598
19599     // private
19600     fixKeys : function(){ // load time branching for fastest keydown performance
19601         if(Roo.isIE){
19602             return function(e){
19603                 var k = e.getKey(), r;
19604                 if(k == e.TAB){
19605                     e.stopEvent();
19606                     r = this.doc.selection.createRange();
19607                     if(r){
19608                         r.collapse(true);
19609                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19610                         this.deferFocus();
19611                     }
19612                     return;
19613                 }
19614                 
19615                 if(k == e.ENTER){
19616                     r = this.doc.selection.createRange();
19617                     if(r){
19618                         var target = r.parentElement();
19619                         if(!target || target.tagName.toLowerCase() != 'li'){
19620                             e.stopEvent();
19621                             r.pasteHTML('<br />');
19622                             r.collapse(false);
19623                             r.select();
19624                         }
19625                     }
19626                 }
19627                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19628                     this.cleanUpPaste.defer(100, this);
19629                     return;
19630                 }
19631                 
19632                 
19633             };
19634         }else if(Roo.isOpera){
19635             return function(e){
19636                 var k = e.getKey();
19637                 if(k == e.TAB){
19638                     e.stopEvent();
19639                     this.win.focus();
19640                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19641                     this.deferFocus();
19642                 }
19643                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19644                     this.cleanUpPaste.defer(100, this);
19645                     return;
19646                 }
19647                 
19648             };
19649         }else if(Roo.isSafari){
19650             return function(e){
19651                 var k = e.getKey();
19652                 
19653                 if(k == e.TAB){
19654                     e.stopEvent();
19655                     this.execCmd('InsertText','\t');
19656                     this.deferFocus();
19657                     return;
19658                 }
19659                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19660                     this.cleanUpPaste.defer(100, this);
19661                     return;
19662                 }
19663                 
19664              };
19665         }
19666     }(),
19667     
19668     getAllAncestors: function()
19669     {
19670         var p = this.getSelectedNode();
19671         var a = [];
19672         if (!p) {
19673             a.push(p); // push blank onto stack..
19674             p = this.getParentElement();
19675         }
19676         
19677         
19678         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19679             a.push(p);
19680             p = p.parentNode;
19681         }
19682         a.push(this.doc.body);
19683         return a;
19684     },
19685     lastSel : false,
19686     lastSelNode : false,
19687     
19688     
19689     getSelection : function() 
19690     {
19691         this.assignDocWin();
19692         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19693     },
19694     
19695     getSelectedNode: function() 
19696     {
19697         // this may only work on Gecko!!!
19698         
19699         // should we cache this!!!!
19700         
19701         
19702         
19703          
19704         var range = this.createRange(this.getSelection()).cloneRange();
19705         
19706         if (Roo.isIE) {
19707             var parent = range.parentElement();
19708             while (true) {
19709                 var testRange = range.duplicate();
19710                 testRange.moveToElementText(parent);
19711                 if (testRange.inRange(range)) {
19712                     break;
19713                 }
19714                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19715                     break;
19716                 }
19717                 parent = parent.parentElement;
19718             }
19719             return parent;
19720         }
19721         
19722         // is ancestor a text element.
19723         var ac =  range.commonAncestorContainer;
19724         if (ac.nodeType == 3) {
19725             ac = ac.parentNode;
19726         }
19727         
19728         var ar = ac.childNodes;
19729          
19730         var nodes = [];
19731         var other_nodes = [];
19732         var has_other_nodes = false;
19733         for (var i=0;i<ar.length;i++) {
19734             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19735                 continue;
19736             }
19737             // fullly contained node.
19738             
19739             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19740                 nodes.push(ar[i]);
19741                 continue;
19742             }
19743             
19744             // probably selected..
19745             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19746                 other_nodes.push(ar[i]);
19747                 continue;
19748             }
19749             // outer..
19750             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19751                 continue;
19752             }
19753             
19754             
19755             has_other_nodes = true;
19756         }
19757         if (!nodes.length && other_nodes.length) {
19758             nodes= other_nodes;
19759         }
19760         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19761             return false;
19762         }
19763         
19764         return nodes[0];
19765     },
19766     createRange: function(sel)
19767     {
19768         // this has strange effects when using with 
19769         // top toolbar - not sure if it's a great idea.
19770         //this.editor.contentWindow.focus();
19771         if (typeof sel != "undefined") {
19772             try {
19773                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19774             } catch(e) {
19775                 return this.doc.createRange();
19776             }
19777         } else {
19778             return this.doc.createRange();
19779         }
19780     },
19781     getParentElement: function()
19782     {
19783         
19784         this.assignDocWin();
19785         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19786         
19787         var range = this.createRange(sel);
19788          
19789         try {
19790             var p = range.commonAncestorContainer;
19791             while (p.nodeType == 3) { // text node
19792                 p = p.parentNode;
19793             }
19794             return p;
19795         } catch (e) {
19796             return null;
19797         }
19798     
19799     },
19800     /***
19801      *
19802      * Range intersection.. the hard stuff...
19803      *  '-1' = before
19804      *  '0' = hits..
19805      *  '1' = after.
19806      *         [ -- selected range --- ]
19807      *   [fail]                        [fail]
19808      *
19809      *    basically..
19810      *      if end is before start or  hits it. fail.
19811      *      if start is after end or hits it fail.
19812      *
19813      *   if either hits (but other is outside. - then it's not 
19814      *   
19815      *    
19816      **/
19817     
19818     
19819     // @see http://www.thismuchiknow.co.uk/?p=64.
19820     rangeIntersectsNode : function(range, node)
19821     {
19822         var nodeRange = node.ownerDocument.createRange();
19823         try {
19824             nodeRange.selectNode(node);
19825         } catch (e) {
19826             nodeRange.selectNodeContents(node);
19827         }
19828     
19829         var rangeStartRange = range.cloneRange();
19830         rangeStartRange.collapse(true);
19831     
19832         var rangeEndRange = range.cloneRange();
19833         rangeEndRange.collapse(false);
19834     
19835         var nodeStartRange = nodeRange.cloneRange();
19836         nodeStartRange.collapse(true);
19837     
19838         var nodeEndRange = nodeRange.cloneRange();
19839         nodeEndRange.collapse(false);
19840     
19841         return rangeStartRange.compareBoundaryPoints(
19842                  Range.START_TO_START, nodeEndRange) == -1 &&
19843                rangeEndRange.compareBoundaryPoints(
19844                  Range.START_TO_START, nodeStartRange) == 1;
19845         
19846          
19847     },
19848     rangeCompareNode : function(range, node)
19849     {
19850         var nodeRange = node.ownerDocument.createRange();
19851         try {
19852             nodeRange.selectNode(node);
19853         } catch (e) {
19854             nodeRange.selectNodeContents(node);
19855         }
19856         
19857         
19858         range.collapse(true);
19859     
19860         nodeRange.collapse(true);
19861      
19862         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19863         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19864          
19865         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19866         
19867         var nodeIsBefore   =  ss == 1;
19868         var nodeIsAfter    = ee == -1;
19869         
19870         if (nodeIsBefore && nodeIsAfter)
19871             return 0; // outer
19872         if (!nodeIsBefore && nodeIsAfter)
19873             return 1; //right trailed.
19874         
19875         if (nodeIsBefore && !nodeIsAfter)
19876             return 2;  // left trailed.
19877         // fully contined.
19878         return 3;
19879     },
19880
19881     // private? - in a new class?
19882     cleanUpPaste :  function()
19883     {
19884         // cleans up the whole document..
19885         Roo.log('cleanuppaste');
19886         
19887         this.cleanUpChildren(this.doc.body);
19888         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19889         if (clean != this.doc.body.innerHTML) {
19890             this.doc.body.innerHTML = clean;
19891         }
19892         
19893     },
19894     
19895     cleanWordChars : function(input) {// change the chars to hex code
19896         var he = Roo.HtmlEditorCore;
19897         
19898         var output = input;
19899         Roo.each(he.swapCodes, function(sw) { 
19900             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19901             
19902             output = output.replace(swapper, sw[1]);
19903         });
19904         
19905         return output;
19906     },
19907     
19908     
19909     cleanUpChildren : function (n)
19910     {
19911         if (!n.childNodes.length) {
19912             return;
19913         }
19914         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19915            this.cleanUpChild(n.childNodes[i]);
19916         }
19917     },
19918     
19919     
19920         
19921     
19922     cleanUpChild : function (node)
19923     {
19924         var ed = this;
19925         //console.log(node);
19926         if (node.nodeName == "#text") {
19927             // clean up silly Windows -- stuff?
19928             return; 
19929         }
19930         if (node.nodeName == "#comment") {
19931             node.parentNode.removeChild(node);
19932             // clean up silly Windows -- stuff?
19933             return; 
19934         }
19935         var lcname = node.tagName.toLowerCase();
19936         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19937         // whitelist of tags..
19938         
19939         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19940             // remove node.
19941             node.parentNode.removeChild(node);
19942             return;
19943             
19944         }
19945         
19946         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19947         
19948         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19949         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19950         
19951         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19952         //    remove_keep_children = true;
19953         //}
19954         
19955         if (remove_keep_children) {
19956             this.cleanUpChildren(node);
19957             // inserts everything just before this node...
19958             while (node.childNodes.length) {
19959                 var cn = node.childNodes[0];
19960                 node.removeChild(cn);
19961                 node.parentNode.insertBefore(cn, node);
19962             }
19963             node.parentNode.removeChild(node);
19964             return;
19965         }
19966         
19967         if (!node.attributes || !node.attributes.length) {
19968             this.cleanUpChildren(node);
19969             return;
19970         }
19971         
19972         function cleanAttr(n,v)
19973         {
19974             
19975             if (v.match(/^\./) || v.match(/^\//)) {
19976                 return;
19977             }
19978             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19979                 return;
19980             }
19981             if (v.match(/^#/)) {
19982                 return;
19983             }
19984 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19985             node.removeAttribute(n);
19986             
19987         }
19988         
19989         var cwhite = this.cwhite;
19990         var cblack = this.cblack;
19991             
19992         function cleanStyle(n,v)
19993         {
19994             if (v.match(/expression/)) { //XSS?? should we even bother..
19995                 node.removeAttribute(n);
19996                 return;
19997             }
19998             
19999             var parts = v.split(/;/);
20000             var clean = [];
20001             
20002             Roo.each(parts, function(p) {
20003                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20004                 if (!p.length) {
20005                     return true;
20006                 }
20007                 var l = p.split(':').shift().replace(/\s+/g,'');
20008                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20009                 
20010                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20011 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20012                     //node.removeAttribute(n);
20013                     return true;
20014                 }
20015                 //Roo.log()
20016                 // only allow 'c whitelisted system attributes'
20017                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20018 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20019                     //node.removeAttribute(n);
20020                     return true;
20021                 }
20022                 
20023                 
20024                  
20025                 
20026                 clean.push(p);
20027                 return true;
20028             });
20029             if (clean.length) { 
20030                 node.setAttribute(n, clean.join(';'));
20031             } else {
20032                 node.removeAttribute(n);
20033             }
20034             
20035         }
20036         
20037         
20038         for (var i = node.attributes.length-1; i > -1 ; i--) {
20039             var a = node.attributes[i];
20040             //console.log(a);
20041             
20042             if (a.name.toLowerCase().substr(0,2)=='on')  {
20043                 node.removeAttribute(a.name);
20044                 continue;
20045             }
20046             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20047                 node.removeAttribute(a.name);
20048                 continue;
20049             }
20050             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20051                 cleanAttr(a.name,a.value); // fixme..
20052                 continue;
20053             }
20054             if (a.name == 'style') {
20055                 cleanStyle(a.name,a.value);
20056                 continue;
20057             }
20058             /// clean up MS crap..
20059             // tecnically this should be a list of valid class'es..
20060             
20061             
20062             if (a.name == 'class') {
20063                 if (a.value.match(/^Mso/)) {
20064                     node.className = '';
20065                 }
20066                 
20067                 if (a.value.match(/body/)) {
20068                     node.className = '';
20069                 }
20070                 continue;
20071             }
20072             
20073             // style cleanup!?
20074             // class cleanup?
20075             
20076         }
20077         
20078         
20079         this.cleanUpChildren(node);
20080         
20081         
20082     },
20083     
20084     /**
20085      * Clean up MS wordisms...
20086      */
20087     cleanWord : function(node)
20088     {
20089         
20090         
20091         if (!node) {
20092             this.cleanWord(this.doc.body);
20093             return;
20094         }
20095         if (node.nodeName == "#text") {
20096             // clean up silly Windows -- stuff?
20097             return; 
20098         }
20099         if (node.nodeName == "#comment") {
20100             node.parentNode.removeChild(node);
20101             // clean up silly Windows -- stuff?
20102             return; 
20103         }
20104         
20105         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20106             node.parentNode.removeChild(node);
20107             return;
20108         }
20109         
20110         // remove - but keep children..
20111         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20112             while (node.childNodes.length) {
20113                 var cn = node.childNodes[0];
20114                 node.removeChild(cn);
20115                 node.parentNode.insertBefore(cn, node);
20116             }
20117             node.parentNode.removeChild(node);
20118             this.iterateChildren(node, this.cleanWord);
20119             return;
20120         }
20121         // clean styles
20122         if (node.className.length) {
20123             
20124             var cn = node.className.split(/\W+/);
20125             var cna = [];
20126             Roo.each(cn, function(cls) {
20127                 if (cls.match(/Mso[a-zA-Z]+/)) {
20128                     return;
20129                 }
20130                 cna.push(cls);
20131             });
20132             node.className = cna.length ? cna.join(' ') : '';
20133             if (!cna.length) {
20134                 node.removeAttribute("class");
20135             }
20136         }
20137         
20138         if (node.hasAttribute("lang")) {
20139             node.removeAttribute("lang");
20140         }
20141         
20142         if (node.hasAttribute("style")) {
20143             
20144             var styles = node.getAttribute("style").split(";");
20145             var nstyle = [];
20146             Roo.each(styles, function(s) {
20147                 if (!s.match(/:/)) {
20148                     return;
20149                 }
20150                 var kv = s.split(":");
20151                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20152                     return;
20153                 }
20154                 // what ever is left... we allow.
20155                 nstyle.push(s);
20156             });
20157             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20158             if (!nstyle.length) {
20159                 node.removeAttribute('style');
20160             }
20161         }
20162         this.iterateChildren(node, this.cleanWord);
20163         
20164         
20165         
20166     },
20167     /**
20168      * iterateChildren of a Node, calling fn each time, using this as the scole..
20169      * @param {DomNode} node node to iterate children of.
20170      * @param {Function} fn method of this class to call on each item.
20171      */
20172     iterateChildren : function(node, fn)
20173     {
20174         if (!node.childNodes.length) {
20175                 return;
20176         }
20177         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20178            fn.call(this, node.childNodes[i])
20179         }
20180     },
20181     
20182     
20183     /**
20184      * cleanTableWidths.
20185      *
20186      * Quite often pasting from word etc.. results in tables with column and widths.
20187      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20188      *
20189      */
20190     cleanTableWidths : function(node)
20191     {
20192          
20193          
20194         if (!node) {
20195             this.cleanTableWidths(this.doc.body);
20196             return;
20197         }
20198         
20199         // ignore list...
20200         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20201             return; 
20202         }
20203         Roo.log(node.tagName);
20204         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20205             this.iterateChildren(node, this.cleanTableWidths);
20206             return;
20207         }
20208         if (node.hasAttribute('width')) {
20209             node.removeAttribute('width');
20210         }
20211         
20212          
20213         if (node.hasAttribute("style")) {
20214             // pretty basic...
20215             
20216             var styles = node.getAttribute("style").split(";");
20217             var nstyle = [];
20218             Roo.each(styles, function(s) {
20219                 if (!s.match(/:/)) {
20220                     return;
20221                 }
20222                 var kv = s.split(":");
20223                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20224                     return;
20225                 }
20226                 // what ever is left... we allow.
20227                 nstyle.push(s);
20228             });
20229             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20230             if (!nstyle.length) {
20231                 node.removeAttribute('style');
20232             }
20233         }
20234         
20235         this.iterateChildren(node, this.cleanTableWidths);
20236         
20237         
20238     },
20239     
20240     
20241     
20242     
20243     domToHTML : function(currentElement, depth, nopadtext) {
20244         
20245         depth = depth || 0;
20246         nopadtext = nopadtext || false;
20247     
20248         if (!currentElement) {
20249             return this.domToHTML(this.doc.body);
20250         }
20251         
20252         //Roo.log(currentElement);
20253         var j;
20254         var allText = false;
20255         var nodeName = currentElement.nodeName;
20256         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20257         
20258         if  (nodeName == '#text') {
20259             
20260             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20261         }
20262         
20263         
20264         var ret = '';
20265         if (nodeName != 'BODY') {
20266              
20267             var i = 0;
20268             // Prints the node tagName, such as <A>, <IMG>, etc
20269             if (tagName) {
20270                 var attr = [];
20271                 for(i = 0; i < currentElement.attributes.length;i++) {
20272                     // quoting?
20273                     var aname = currentElement.attributes.item(i).name;
20274                     if (!currentElement.attributes.item(i).value.length) {
20275                         continue;
20276                     }
20277                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20278                 }
20279                 
20280                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20281             } 
20282             else {
20283                 
20284                 // eack
20285             }
20286         } else {
20287             tagName = false;
20288         }
20289         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20290             return ret;
20291         }
20292         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20293             nopadtext = true;
20294         }
20295         
20296         
20297         // Traverse the tree
20298         i = 0;
20299         var currentElementChild = currentElement.childNodes.item(i);
20300         var allText = true;
20301         var innerHTML  = '';
20302         lastnode = '';
20303         while (currentElementChild) {
20304             // Formatting code (indent the tree so it looks nice on the screen)
20305             var nopad = nopadtext;
20306             if (lastnode == 'SPAN') {
20307                 nopad  = true;
20308             }
20309             // text
20310             if  (currentElementChild.nodeName == '#text') {
20311                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20312                 toadd = nopadtext ? toadd : toadd.trim();
20313                 if (!nopad && toadd.length > 80) {
20314                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20315                 }
20316                 innerHTML  += toadd;
20317                 
20318                 i++;
20319                 currentElementChild = currentElement.childNodes.item(i);
20320                 lastNode = '';
20321                 continue;
20322             }
20323             allText = false;
20324             
20325             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20326                 
20327             // Recursively traverse the tree structure of the child node
20328             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20329             lastnode = currentElementChild.nodeName;
20330             i++;
20331             currentElementChild=currentElement.childNodes.item(i);
20332         }
20333         
20334         ret += innerHTML;
20335         
20336         if (!allText) {
20337                 // The remaining code is mostly for formatting the tree
20338             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20339         }
20340         
20341         
20342         if (tagName) {
20343             ret+= "</"+tagName+">";
20344         }
20345         return ret;
20346         
20347     },
20348         
20349     applyBlacklists : function()
20350     {
20351         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20352         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20353         
20354         this.white = [];
20355         this.black = [];
20356         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20357             if (b.indexOf(tag) > -1) {
20358                 return;
20359             }
20360             this.white.push(tag);
20361             
20362         }, this);
20363         
20364         Roo.each(w, function(tag) {
20365             if (b.indexOf(tag) > -1) {
20366                 return;
20367             }
20368             if (this.white.indexOf(tag) > -1) {
20369                 return;
20370             }
20371             this.white.push(tag);
20372             
20373         }, this);
20374         
20375         
20376         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20377             if (w.indexOf(tag) > -1) {
20378                 return;
20379             }
20380             this.black.push(tag);
20381             
20382         }, this);
20383         
20384         Roo.each(b, function(tag) {
20385             if (w.indexOf(tag) > -1) {
20386                 return;
20387             }
20388             if (this.black.indexOf(tag) > -1) {
20389                 return;
20390             }
20391             this.black.push(tag);
20392             
20393         }, this);
20394         
20395         
20396         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20397         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20398         
20399         this.cwhite = [];
20400         this.cblack = [];
20401         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20402             if (b.indexOf(tag) > -1) {
20403                 return;
20404             }
20405             this.cwhite.push(tag);
20406             
20407         }, this);
20408         
20409         Roo.each(w, function(tag) {
20410             if (b.indexOf(tag) > -1) {
20411                 return;
20412             }
20413             if (this.cwhite.indexOf(tag) > -1) {
20414                 return;
20415             }
20416             this.cwhite.push(tag);
20417             
20418         }, this);
20419         
20420         
20421         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20422             if (w.indexOf(tag) > -1) {
20423                 return;
20424             }
20425             this.cblack.push(tag);
20426             
20427         }, this);
20428         
20429         Roo.each(b, function(tag) {
20430             if (w.indexOf(tag) > -1) {
20431                 return;
20432             }
20433             if (this.cblack.indexOf(tag) > -1) {
20434                 return;
20435             }
20436             this.cblack.push(tag);
20437             
20438         }, this);
20439     },
20440     
20441     setStylesheets : function(stylesheets)
20442     {
20443         if(typeof(stylesheets) == 'string'){
20444             Roo.get(this.iframe.contentDocument.head).createChild({
20445                 tag : 'link',
20446                 rel : 'stylesheet',
20447                 type : 'text/css',
20448                 href : stylesheets
20449             });
20450             
20451             return;
20452         }
20453         var _this = this;
20454      
20455         Roo.each(stylesheets, function(s) {
20456             if(!s.length){
20457                 return;
20458             }
20459             
20460             Roo.get(_this.iframe.contentDocument.head).createChild({
20461                 tag : 'link',
20462                 rel : 'stylesheet',
20463                 type : 'text/css',
20464                 href : s
20465             });
20466         });
20467
20468         
20469     },
20470     
20471     removeStylesheets : function()
20472     {
20473         var _this = this;
20474         
20475         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20476             s.remove();
20477         });
20478     }
20479     
20480     // hide stuff that is not compatible
20481     /**
20482      * @event blur
20483      * @hide
20484      */
20485     /**
20486      * @event change
20487      * @hide
20488      */
20489     /**
20490      * @event focus
20491      * @hide
20492      */
20493     /**
20494      * @event specialkey
20495      * @hide
20496      */
20497     /**
20498      * @cfg {String} fieldClass @hide
20499      */
20500     /**
20501      * @cfg {String} focusClass @hide
20502      */
20503     /**
20504      * @cfg {String} autoCreate @hide
20505      */
20506     /**
20507      * @cfg {String} inputType @hide
20508      */
20509     /**
20510      * @cfg {String} invalidClass @hide
20511      */
20512     /**
20513      * @cfg {String} invalidText @hide
20514      */
20515     /**
20516      * @cfg {String} msgFx @hide
20517      */
20518     /**
20519      * @cfg {String} validateOnBlur @hide
20520      */
20521 });
20522
20523 Roo.HtmlEditorCore.white = [
20524         'area', 'br', 'img', 'input', 'hr', 'wbr',
20525         
20526        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20527        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20528        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20529        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20530        'table',   'ul',         'xmp', 
20531        
20532        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20533       'thead',   'tr', 
20534      
20535       'dir', 'menu', 'ol', 'ul', 'dl',
20536        
20537       'embed',  'object'
20538 ];
20539
20540
20541 Roo.HtmlEditorCore.black = [
20542     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20543         'applet', // 
20544         'base',   'basefont', 'bgsound', 'blink',  'body', 
20545         'frame',  'frameset', 'head',    'html',   'ilayer', 
20546         'iframe', 'layer',  'link',     'meta',    'object',   
20547         'script', 'style' ,'title',  'xml' // clean later..
20548 ];
20549 Roo.HtmlEditorCore.clean = [
20550     'script', 'style', 'title', 'xml'
20551 ];
20552 Roo.HtmlEditorCore.remove = [
20553     'font'
20554 ];
20555 // attributes..
20556
20557 Roo.HtmlEditorCore.ablack = [
20558     'on'
20559 ];
20560     
20561 Roo.HtmlEditorCore.aclean = [ 
20562     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20563 ];
20564
20565 // protocols..
20566 Roo.HtmlEditorCore.pwhite= [
20567         'http',  'https',  'mailto'
20568 ];
20569
20570 // white listed style attributes.
20571 Roo.HtmlEditorCore.cwhite= [
20572       //  'text-align', /// default is to allow most things..
20573       
20574          
20575 //        'font-size'//??
20576 ];
20577
20578 // black listed style attributes.
20579 Roo.HtmlEditorCore.cblack= [
20580       //  'font-size' -- this can be set by the project 
20581 ];
20582
20583
20584 Roo.HtmlEditorCore.swapCodes   =[ 
20585     [    8211, "--" ], 
20586     [    8212, "--" ], 
20587     [    8216,  "'" ],  
20588     [    8217, "'" ],  
20589     [    8220, '"' ],  
20590     [    8221, '"' ],  
20591     [    8226, "*" ],  
20592     [    8230, "..." ]
20593 ]; 
20594
20595     /*
20596  * - LGPL
20597  *
20598  * HtmlEditor
20599  * 
20600  */
20601
20602 /**
20603  * @class Roo.bootstrap.HtmlEditor
20604  * @extends Roo.bootstrap.TextArea
20605  * Bootstrap HtmlEditor class
20606
20607  * @constructor
20608  * Create a new HtmlEditor
20609  * @param {Object} config The config object
20610  */
20611
20612 Roo.bootstrap.HtmlEditor = function(config){
20613     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20614     if (!this.toolbars) {
20615         this.toolbars = [];
20616     }
20617     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20618     this.addEvents({
20619             /**
20620              * @event initialize
20621              * Fires when the editor is fully initialized (including the iframe)
20622              * @param {HtmlEditor} this
20623              */
20624             initialize: true,
20625             /**
20626              * @event activate
20627              * Fires when the editor is first receives the focus. Any insertion must wait
20628              * until after this event.
20629              * @param {HtmlEditor} this
20630              */
20631             activate: true,
20632              /**
20633              * @event beforesync
20634              * Fires before the textarea is updated with content from the editor iframe. Return false
20635              * to cancel the sync.
20636              * @param {HtmlEditor} this
20637              * @param {String} html
20638              */
20639             beforesync: true,
20640              /**
20641              * @event beforepush
20642              * Fires before the iframe editor is updated with content from the textarea. Return false
20643              * to cancel the push.
20644              * @param {HtmlEditor} this
20645              * @param {String} html
20646              */
20647             beforepush: true,
20648              /**
20649              * @event sync
20650              * Fires when the textarea is updated with content from the editor iframe.
20651              * @param {HtmlEditor} this
20652              * @param {String} html
20653              */
20654             sync: true,
20655              /**
20656              * @event push
20657              * Fires when the iframe editor is updated with content from the textarea.
20658              * @param {HtmlEditor} this
20659              * @param {String} html
20660              */
20661             push: true,
20662              /**
20663              * @event editmodechange
20664              * Fires when the editor switches edit modes
20665              * @param {HtmlEditor} this
20666              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20667              */
20668             editmodechange: true,
20669             /**
20670              * @event editorevent
20671              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20672              * @param {HtmlEditor} this
20673              */
20674             editorevent: true,
20675             /**
20676              * @event firstfocus
20677              * Fires when on first focus - needed by toolbars..
20678              * @param {HtmlEditor} this
20679              */
20680             firstfocus: true,
20681             /**
20682              * @event autosave
20683              * Auto save the htmlEditor value as a file into Events
20684              * @param {HtmlEditor} this
20685              */
20686             autosave: true,
20687             /**
20688              * @event savedpreview
20689              * preview the saved version of htmlEditor
20690              * @param {HtmlEditor} this
20691              */
20692             savedpreview: true
20693         });
20694 };
20695
20696
20697 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20698     
20699     
20700       /**
20701      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20702      */
20703     toolbars : false,
20704    
20705      /**
20706      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20707      *                        Roo.resizable.
20708      */
20709     resizable : false,
20710      /**
20711      * @cfg {Number} height (in pixels)
20712      */   
20713     height: 300,
20714    /**
20715      * @cfg {Number} width (in pixels)
20716      */   
20717     width: false,
20718     
20719     /**
20720      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20721      * 
20722      */
20723     stylesheets: false,
20724     
20725     // id of frame..
20726     frameId: false,
20727     
20728     // private properties
20729     validationEvent : false,
20730     deferHeight: true,
20731     initialized : false,
20732     activated : false,
20733     
20734     onFocus : Roo.emptyFn,
20735     iframePad:3,
20736     hideMode:'offsets',
20737     
20738     
20739     tbContainer : false,
20740     
20741     toolbarContainer :function() {
20742         return this.wrap.select('.x-html-editor-tb',true).first();
20743     },
20744
20745     /**
20746      * Protected method that will not generally be called directly. It
20747      * is called when the editor creates its toolbar. Override this method if you need to
20748      * add custom toolbar buttons.
20749      * @param {HtmlEditor} editor
20750      */
20751     createToolbar : function(){
20752         
20753         Roo.log("create toolbars");
20754         
20755         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20756         this.toolbars[0].render(this.toolbarContainer());
20757         
20758         return;
20759         
20760 //        if (!editor.toolbars || !editor.toolbars.length) {
20761 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20762 //        }
20763 //        
20764 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20765 //            editor.toolbars[i] = Roo.factory(
20766 //                    typeof(editor.toolbars[i]) == 'string' ?
20767 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20768 //                Roo.bootstrap.HtmlEditor);
20769 //            editor.toolbars[i].init(editor);
20770 //        }
20771     },
20772
20773      
20774     // private
20775     onRender : function(ct, position)
20776     {
20777        // Roo.log("Call onRender: " + this.xtype);
20778         var _t = this;
20779         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20780       
20781         this.wrap = this.inputEl().wrap({
20782             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20783         });
20784         
20785         this.editorcore.onRender(ct, position);
20786          
20787         if (this.resizable) {
20788             this.resizeEl = new Roo.Resizable(this.wrap, {
20789                 pinned : true,
20790                 wrap: true,
20791                 dynamic : true,
20792                 minHeight : this.height,
20793                 height: this.height,
20794                 handles : this.resizable,
20795                 width: this.width,
20796                 listeners : {
20797                     resize : function(r, w, h) {
20798                         _t.onResize(w,h); // -something
20799                     }
20800                 }
20801             });
20802             
20803         }
20804         this.createToolbar(this);
20805        
20806         
20807         if(!this.width && this.resizable){
20808             this.setSize(this.wrap.getSize());
20809         }
20810         if (this.resizeEl) {
20811             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20812             // should trigger onReize..
20813         }
20814         
20815     },
20816
20817     // private
20818     onResize : function(w, h)
20819     {
20820         Roo.log('resize: ' +w + ',' + h );
20821         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20822         var ew = false;
20823         var eh = false;
20824         
20825         if(this.inputEl() ){
20826             if(typeof w == 'number'){
20827                 var aw = w - this.wrap.getFrameWidth('lr');
20828                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20829                 ew = aw;
20830             }
20831             if(typeof h == 'number'){
20832                  var tbh = -11;  // fixme it needs to tool bar size!
20833                 for (var i =0; i < this.toolbars.length;i++) {
20834                     // fixme - ask toolbars for heights?
20835                     tbh += this.toolbars[i].el.getHeight();
20836                     //if (this.toolbars[i].footer) {
20837                     //    tbh += this.toolbars[i].footer.el.getHeight();
20838                     //}
20839                 }
20840               
20841                 
20842                 
20843                 
20844                 
20845                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20846                 ah -= 5; // knock a few pixes off for look..
20847                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20848                 var eh = ah;
20849             }
20850         }
20851         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20852         this.editorcore.onResize(ew,eh);
20853         
20854     },
20855
20856     /**
20857      * Toggles the editor between standard and source edit mode.
20858      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20859      */
20860     toggleSourceEdit : function(sourceEditMode)
20861     {
20862         this.editorcore.toggleSourceEdit(sourceEditMode);
20863         
20864         if(this.editorcore.sourceEditMode){
20865             Roo.log('editor - showing textarea');
20866             
20867 //            Roo.log('in');
20868 //            Roo.log(this.syncValue());
20869             this.syncValue();
20870             this.inputEl().removeClass(['hide', 'x-hidden']);
20871             this.inputEl().dom.removeAttribute('tabIndex');
20872             this.inputEl().focus();
20873         }else{
20874             Roo.log('editor - hiding textarea');
20875 //            Roo.log('out')
20876 //            Roo.log(this.pushValue()); 
20877             this.pushValue();
20878             
20879             this.inputEl().addClass(['hide', 'x-hidden']);
20880             this.inputEl().dom.setAttribute('tabIndex', -1);
20881             //this.deferFocus();
20882         }
20883          
20884         if(this.resizable){
20885             this.setSize(this.wrap.getSize());
20886         }
20887         
20888         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20889     },
20890  
20891     // private (for BoxComponent)
20892     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20893
20894     // private (for BoxComponent)
20895     getResizeEl : function(){
20896         return this.wrap;
20897     },
20898
20899     // private (for BoxComponent)
20900     getPositionEl : function(){
20901         return this.wrap;
20902     },
20903
20904     // private
20905     initEvents : function(){
20906         this.originalValue = this.getValue();
20907     },
20908
20909 //    /**
20910 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20911 //     * @method
20912 //     */
20913 //    markInvalid : Roo.emptyFn,
20914 //    /**
20915 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20916 //     * @method
20917 //     */
20918 //    clearInvalid : Roo.emptyFn,
20919
20920     setValue : function(v){
20921         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20922         this.editorcore.pushValue();
20923     },
20924
20925      
20926     // private
20927     deferFocus : function(){
20928         this.focus.defer(10, this);
20929     },
20930
20931     // doc'ed in Field
20932     focus : function(){
20933         this.editorcore.focus();
20934         
20935     },
20936       
20937
20938     // private
20939     onDestroy : function(){
20940         
20941         
20942         
20943         if(this.rendered){
20944             
20945             for (var i =0; i < this.toolbars.length;i++) {
20946                 // fixme - ask toolbars for heights?
20947                 this.toolbars[i].onDestroy();
20948             }
20949             
20950             this.wrap.dom.innerHTML = '';
20951             this.wrap.remove();
20952         }
20953     },
20954
20955     // private
20956     onFirstFocus : function(){
20957         //Roo.log("onFirstFocus");
20958         this.editorcore.onFirstFocus();
20959          for (var i =0; i < this.toolbars.length;i++) {
20960             this.toolbars[i].onFirstFocus();
20961         }
20962         
20963     },
20964     
20965     // private
20966     syncValue : function()
20967     {   
20968         this.editorcore.syncValue();
20969     },
20970     
20971     pushValue : function()
20972     {   
20973         this.editorcore.pushValue();
20974     }
20975      
20976     
20977     // hide stuff that is not compatible
20978     /**
20979      * @event blur
20980      * @hide
20981      */
20982     /**
20983      * @event change
20984      * @hide
20985      */
20986     /**
20987      * @event focus
20988      * @hide
20989      */
20990     /**
20991      * @event specialkey
20992      * @hide
20993      */
20994     /**
20995      * @cfg {String} fieldClass @hide
20996      */
20997     /**
20998      * @cfg {String} focusClass @hide
20999      */
21000     /**
21001      * @cfg {String} autoCreate @hide
21002      */
21003     /**
21004      * @cfg {String} inputType @hide
21005      */
21006     /**
21007      * @cfg {String} invalidClass @hide
21008      */
21009     /**
21010      * @cfg {String} invalidText @hide
21011      */
21012     /**
21013      * @cfg {String} msgFx @hide
21014      */
21015     /**
21016      * @cfg {String} validateOnBlur @hide
21017      */
21018 });
21019  
21020     
21021    
21022    
21023    
21024       
21025 Roo.namespace('Roo.bootstrap.htmleditor');
21026 /**
21027  * @class Roo.bootstrap.HtmlEditorToolbar1
21028  * Basic Toolbar
21029  * 
21030  * Usage:
21031  *
21032  new Roo.bootstrap.HtmlEditor({
21033     ....
21034     toolbars : [
21035         new Roo.bootstrap.HtmlEditorToolbar1({
21036             disable : { fonts: 1 , format: 1, ..., ... , ...],
21037             btns : [ .... ]
21038         })
21039     }
21040      
21041  * 
21042  * @cfg {Object} disable List of elements to disable..
21043  * @cfg {Array} btns List of additional buttons.
21044  * 
21045  * 
21046  * NEEDS Extra CSS? 
21047  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21048  */
21049  
21050 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21051 {
21052     
21053     Roo.apply(this, config);
21054     
21055     // default disabled, based on 'good practice'..
21056     this.disable = this.disable || {};
21057     Roo.applyIf(this.disable, {
21058         fontSize : true,
21059         colors : true,
21060         specialElements : true
21061     });
21062     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21063     
21064     this.editor = config.editor;
21065     this.editorcore = config.editor.editorcore;
21066     
21067     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21068     
21069     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21070     // dont call parent... till later.
21071 }
21072 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21073      
21074     bar : true,
21075     
21076     editor : false,
21077     editorcore : false,
21078     
21079     
21080     formats : [
21081         "p" ,  
21082         "h1","h2","h3","h4","h5","h6", 
21083         "pre", "code", 
21084         "abbr", "acronym", "address", "cite", "samp", "var",
21085         'div','span'
21086     ],
21087     
21088     onRender : function(ct, position)
21089     {
21090        // Roo.log("Call onRender: " + this.xtype);
21091         
21092        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21093        Roo.log(this.el);
21094        this.el.dom.style.marginBottom = '0';
21095        var _this = this;
21096        var editorcore = this.editorcore;
21097        var editor= this.editor;
21098        
21099        var children = [];
21100        var btn = function(id,cmd , toggle, handler){
21101        
21102             var  event = toggle ? 'toggle' : 'click';
21103        
21104             var a = {
21105                 size : 'sm',
21106                 xtype: 'Button',
21107                 xns: Roo.bootstrap,
21108                 glyphicon : id,
21109                 cmd : id || cmd,
21110                 enableToggle:toggle !== false,
21111                 //html : 'submit'
21112                 pressed : toggle ? false : null,
21113                 listeners : {}
21114             };
21115             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21116                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21117             };
21118             children.push(a);
21119             return a;
21120        }
21121         
21122         var style = {
21123                 xtype: 'Button',
21124                 size : 'sm',
21125                 xns: Roo.bootstrap,
21126                 glyphicon : 'font',
21127                 //html : 'submit'
21128                 menu : {
21129                     xtype: 'Menu',
21130                     xns: Roo.bootstrap,
21131                     items:  []
21132                 }
21133         };
21134         Roo.each(this.formats, function(f) {
21135             style.menu.items.push({
21136                 xtype :'MenuItem',
21137                 xns: Roo.bootstrap,
21138                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21139                 tagname : f,
21140                 listeners : {
21141                     click : function()
21142                     {
21143                         editorcore.insertTag(this.tagname);
21144                         editor.focus();
21145                     }
21146                 }
21147                 
21148             });
21149         });
21150          children.push(style);   
21151             
21152             
21153         btn('bold',false,true);
21154         btn('italic',false,true);
21155         btn('align-left', 'justifyleft',true);
21156         btn('align-center', 'justifycenter',true);
21157         btn('align-right' , 'justifyright',true);
21158         btn('link', false, false, function(btn) {
21159             //Roo.log("create link?");
21160             var url = prompt(this.createLinkText, this.defaultLinkValue);
21161             if(url && url != 'http:/'+'/'){
21162                 this.editorcore.relayCmd('createlink', url);
21163             }
21164         }),
21165         btn('list','insertunorderedlist',true);
21166         btn('pencil', false,true, function(btn){
21167                 Roo.log(this);
21168                 
21169                 this.toggleSourceEdit(btn.pressed);
21170         });
21171         /*
21172         var cog = {
21173                 xtype: 'Button',
21174                 size : 'sm',
21175                 xns: Roo.bootstrap,
21176                 glyphicon : 'cog',
21177                 //html : 'submit'
21178                 menu : {
21179                     xtype: 'Menu',
21180                     xns: Roo.bootstrap,
21181                     items:  []
21182                 }
21183         };
21184         
21185         cog.menu.items.push({
21186             xtype :'MenuItem',
21187             xns: Roo.bootstrap,
21188             html : Clean styles,
21189             tagname : f,
21190             listeners : {
21191                 click : function()
21192                 {
21193                     editorcore.insertTag(this.tagname);
21194                     editor.focus();
21195                 }
21196             }
21197             
21198         });
21199        */
21200         
21201          
21202        this.xtype = 'NavSimplebar';
21203         
21204         for(var i=0;i< children.length;i++) {
21205             
21206             this.buttons.add(this.addxtypeChild(children[i]));
21207             
21208         }
21209         
21210         editor.on('editorevent', this.updateToolbar, this);
21211     },
21212     onBtnClick : function(id)
21213     {
21214        this.editorcore.relayCmd(id);
21215        this.editorcore.focus();
21216     },
21217     
21218     /**
21219      * Protected method that will not generally be called directly. It triggers
21220      * a toolbar update by reading the markup state of the current selection in the editor.
21221      */
21222     updateToolbar: function(){
21223
21224         if(!this.editorcore.activated){
21225             this.editor.onFirstFocus(); // is this neeed?
21226             return;
21227         }
21228
21229         var btns = this.buttons; 
21230         var doc = this.editorcore.doc;
21231         btns.get('bold').setActive(doc.queryCommandState('bold'));
21232         btns.get('italic').setActive(doc.queryCommandState('italic'));
21233         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21234         
21235         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21236         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21237         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21238         
21239         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21240         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21241          /*
21242         
21243         var ans = this.editorcore.getAllAncestors();
21244         if (this.formatCombo) {
21245             
21246             
21247             var store = this.formatCombo.store;
21248             this.formatCombo.setValue("");
21249             for (var i =0; i < ans.length;i++) {
21250                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21251                     // select it..
21252                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21253                     break;
21254                 }
21255             }
21256         }
21257         
21258         
21259         
21260         // hides menus... - so this cant be on a menu...
21261         Roo.bootstrap.MenuMgr.hideAll();
21262         */
21263         Roo.bootstrap.MenuMgr.hideAll();
21264         //this.editorsyncValue();
21265     },
21266     onFirstFocus: function() {
21267         this.buttons.each(function(item){
21268            item.enable();
21269         });
21270     },
21271     toggleSourceEdit : function(sourceEditMode){
21272         
21273           
21274         if(sourceEditMode){
21275             Roo.log("disabling buttons");
21276            this.buttons.each( function(item){
21277                 if(item.cmd != 'pencil'){
21278                     item.disable();
21279                 }
21280             });
21281           
21282         }else{
21283             Roo.log("enabling buttons");
21284             if(this.editorcore.initialized){
21285                 this.buttons.each( function(item){
21286                     item.enable();
21287                 });
21288             }
21289             
21290         }
21291         Roo.log("calling toggole on editor");
21292         // tell the editor that it's been pressed..
21293         this.editor.toggleSourceEdit(sourceEditMode);
21294        
21295     }
21296 });
21297
21298
21299
21300
21301
21302 /**
21303  * @class Roo.bootstrap.Table.AbstractSelectionModel
21304  * @extends Roo.util.Observable
21305  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21306  * implemented by descendant classes.  This class should not be directly instantiated.
21307  * @constructor
21308  */
21309 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21310     this.locked = false;
21311     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21312 };
21313
21314
21315 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21316     /** @ignore Called by the grid automatically. Do not call directly. */
21317     init : function(grid){
21318         this.grid = grid;
21319         this.initEvents();
21320     },
21321
21322     /**
21323      * Locks the selections.
21324      */
21325     lock : function(){
21326         this.locked = true;
21327     },
21328
21329     /**
21330      * Unlocks the selections.
21331      */
21332     unlock : function(){
21333         this.locked = false;
21334     },
21335
21336     /**
21337      * Returns true if the selections are locked.
21338      * @return {Boolean}
21339      */
21340     isLocked : function(){
21341         return this.locked;
21342     }
21343 });
21344 /**
21345  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21346  * @class Roo.bootstrap.Table.RowSelectionModel
21347  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21348  * It supports multiple selections and keyboard selection/navigation. 
21349  * @constructor
21350  * @param {Object} config
21351  */
21352
21353 Roo.bootstrap.Table.RowSelectionModel = function(config){
21354     Roo.apply(this, config);
21355     this.selections = new Roo.util.MixedCollection(false, function(o){
21356         return o.id;
21357     });
21358
21359     this.last = false;
21360     this.lastActive = false;
21361
21362     this.addEvents({
21363         /**
21364              * @event selectionchange
21365              * Fires when the selection changes
21366              * @param {SelectionModel} this
21367              */
21368             "selectionchange" : true,
21369         /**
21370              * @event afterselectionchange
21371              * Fires after the selection changes (eg. by key press or clicking)
21372              * @param {SelectionModel} this
21373              */
21374             "afterselectionchange" : true,
21375         /**
21376              * @event beforerowselect
21377              * Fires when a row is selected being selected, return false to cancel.
21378              * @param {SelectionModel} this
21379              * @param {Number} rowIndex The selected index
21380              * @param {Boolean} keepExisting False if other selections will be cleared
21381              */
21382             "beforerowselect" : true,
21383         /**
21384              * @event rowselect
21385              * Fires when a row is selected.
21386              * @param {SelectionModel} this
21387              * @param {Number} rowIndex The selected index
21388              * @param {Roo.data.Record} r The record
21389              */
21390             "rowselect" : true,
21391         /**
21392              * @event rowdeselect
21393              * Fires when a row is deselected.
21394              * @param {SelectionModel} this
21395              * @param {Number} rowIndex The selected index
21396              */
21397         "rowdeselect" : true
21398     });
21399     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21400     this.locked = false;
21401 };
21402
21403 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21404     /**
21405      * @cfg {Boolean} singleSelect
21406      * True to allow selection of only one row at a time (defaults to false)
21407      */
21408     singleSelect : false,
21409
21410     // private
21411     initEvents : function(){
21412
21413         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21414             this.grid.on("mousedown", this.handleMouseDown, this);
21415         }else{ // allow click to work like normal
21416             this.grid.on("rowclick", this.handleDragableRowClick, this);
21417         }
21418
21419         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21420             "up" : function(e){
21421                 if(!e.shiftKey){
21422                     this.selectPrevious(e.shiftKey);
21423                 }else if(this.last !== false && this.lastActive !== false){
21424                     var last = this.last;
21425                     this.selectRange(this.last,  this.lastActive-1);
21426                     this.grid.getView().focusRow(this.lastActive);
21427                     if(last !== false){
21428                         this.last = last;
21429                     }
21430                 }else{
21431                     this.selectFirstRow();
21432                 }
21433                 this.fireEvent("afterselectionchange", this);
21434             },
21435             "down" : function(e){
21436                 if(!e.shiftKey){
21437                     this.selectNext(e.shiftKey);
21438                 }else if(this.last !== false && this.lastActive !== false){
21439                     var last = this.last;
21440                     this.selectRange(this.last,  this.lastActive+1);
21441                     this.grid.getView().focusRow(this.lastActive);
21442                     if(last !== false){
21443                         this.last = last;
21444                     }
21445                 }else{
21446                     this.selectFirstRow();
21447                 }
21448                 this.fireEvent("afterselectionchange", this);
21449             },
21450             scope: this
21451         });
21452
21453         var view = this.grid.view;
21454         view.on("refresh", this.onRefresh, this);
21455         view.on("rowupdated", this.onRowUpdated, this);
21456         view.on("rowremoved", this.onRemove, this);
21457     },
21458
21459     // private
21460     onRefresh : function(){
21461         var ds = this.grid.dataSource, i, v = this.grid.view;
21462         var s = this.selections;
21463         s.each(function(r){
21464             if((i = ds.indexOfId(r.id)) != -1){
21465                 v.onRowSelect(i);
21466             }else{
21467                 s.remove(r);
21468             }
21469         });
21470     },
21471
21472     // private
21473     onRemove : function(v, index, r){
21474         this.selections.remove(r);
21475     },
21476
21477     // private
21478     onRowUpdated : function(v, index, r){
21479         if(this.isSelected(r)){
21480             v.onRowSelect(index);
21481         }
21482     },
21483
21484     /**
21485      * Select records.
21486      * @param {Array} records The records to select
21487      * @param {Boolean} keepExisting (optional) True to keep existing selections
21488      */
21489     selectRecords : function(records, keepExisting){
21490         if(!keepExisting){
21491             this.clearSelections();
21492         }
21493         var ds = this.grid.dataSource;
21494         for(var i = 0, len = records.length; i < len; i++){
21495             this.selectRow(ds.indexOf(records[i]), true);
21496         }
21497     },
21498
21499     /**
21500      * Gets the number of selected rows.
21501      * @return {Number}
21502      */
21503     getCount : function(){
21504         return this.selections.length;
21505     },
21506
21507     /**
21508      * Selects the first row in the grid.
21509      */
21510     selectFirstRow : function(){
21511         this.selectRow(0);
21512     },
21513
21514     /**
21515      * Select the last row.
21516      * @param {Boolean} keepExisting (optional) True to keep existing selections
21517      */
21518     selectLastRow : function(keepExisting){
21519         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21520     },
21521
21522     /**
21523      * Selects the row immediately following the last selected row.
21524      * @param {Boolean} keepExisting (optional) True to keep existing selections
21525      */
21526     selectNext : function(keepExisting){
21527         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21528             this.selectRow(this.last+1, keepExisting);
21529             this.grid.getView().focusRow(this.last);
21530         }
21531     },
21532
21533     /**
21534      * Selects the row that precedes the last selected row.
21535      * @param {Boolean} keepExisting (optional) True to keep existing selections
21536      */
21537     selectPrevious : function(keepExisting){
21538         if(this.last){
21539             this.selectRow(this.last-1, keepExisting);
21540             this.grid.getView().focusRow(this.last);
21541         }
21542     },
21543
21544     /**
21545      * Returns the selected records
21546      * @return {Array} Array of selected records
21547      */
21548     getSelections : function(){
21549         return [].concat(this.selections.items);
21550     },
21551
21552     /**
21553      * Returns the first selected record.
21554      * @return {Record}
21555      */
21556     getSelected : function(){
21557         return this.selections.itemAt(0);
21558     },
21559
21560
21561     /**
21562      * Clears all selections.
21563      */
21564     clearSelections : function(fast){
21565         if(this.locked) return;
21566         if(fast !== true){
21567             var ds = this.grid.dataSource;
21568             var s = this.selections;
21569             s.each(function(r){
21570                 this.deselectRow(ds.indexOfId(r.id));
21571             }, this);
21572             s.clear();
21573         }else{
21574             this.selections.clear();
21575         }
21576         this.last = false;
21577     },
21578
21579
21580     /**
21581      * Selects all rows.
21582      */
21583     selectAll : function(){
21584         if(this.locked) return;
21585         this.selections.clear();
21586         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21587             this.selectRow(i, true);
21588         }
21589     },
21590
21591     /**
21592      * Returns True if there is a selection.
21593      * @return {Boolean}
21594      */
21595     hasSelection : function(){
21596         return this.selections.length > 0;
21597     },
21598
21599     /**
21600      * Returns True if the specified row is selected.
21601      * @param {Number/Record} record The record or index of the record to check
21602      * @return {Boolean}
21603      */
21604     isSelected : function(index){
21605         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21606         return (r && this.selections.key(r.id) ? true : false);
21607     },
21608
21609     /**
21610      * Returns True if the specified record id is selected.
21611      * @param {String} id The id of record to check
21612      * @return {Boolean}
21613      */
21614     isIdSelected : function(id){
21615         return (this.selections.key(id) ? true : false);
21616     },
21617
21618     // private
21619     handleMouseDown : function(e, t){
21620         var view = this.grid.getView(), rowIndex;
21621         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21622             return;
21623         };
21624         if(e.shiftKey && this.last !== false){
21625             var last = this.last;
21626             this.selectRange(last, rowIndex, e.ctrlKey);
21627             this.last = last; // reset the last
21628             view.focusRow(rowIndex);
21629         }else{
21630             var isSelected = this.isSelected(rowIndex);
21631             if(e.button !== 0 && isSelected){
21632                 view.focusRow(rowIndex);
21633             }else if(e.ctrlKey && isSelected){
21634                 this.deselectRow(rowIndex);
21635             }else if(!isSelected){
21636                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21637                 view.focusRow(rowIndex);
21638             }
21639         }
21640         this.fireEvent("afterselectionchange", this);
21641     },
21642     // private
21643     handleDragableRowClick :  function(grid, rowIndex, e) 
21644     {
21645         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21646             this.selectRow(rowIndex, false);
21647             grid.view.focusRow(rowIndex);
21648              this.fireEvent("afterselectionchange", this);
21649         }
21650     },
21651     
21652     /**
21653      * Selects multiple rows.
21654      * @param {Array} rows Array of the indexes of the row to select
21655      * @param {Boolean} keepExisting (optional) True to keep existing selections
21656      */
21657     selectRows : function(rows, keepExisting){
21658         if(!keepExisting){
21659             this.clearSelections();
21660         }
21661         for(var i = 0, len = rows.length; i < len; i++){
21662             this.selectRow(rows[i], true);
21663         }
21664     },
21665
21666     /**
21667      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21668      * @param {Number} startRow The index of the first row in the range
21669      * @param {Number} endRow The index of the last row in the range
21670      * @param {Boolean} keepExisting (optional) True to retain existing selections
21671      */
21672     selectRange : function(startRow, endRow, keepExisting){
21673         if(this.locked) return;
21674         if(!keepExisting){
21675             this.clearSelections();
21676         }
21677         if(startRow <= endRow){
21678             for(var i = startRow; i <= endRow; i++){
21679                 this.selectRow(i, true);
21680             }
21681         }else{
21682             for(var i = startRow; i >= endRow; i--){
21683                 this.selectRow(i, true);
21684             }
21685         }
21686     },
21687
21688     /**
21689      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21690      * @param {Number} startRow The index of the first row in the range
21691      * @param {Number} endRow The index of the last row in the range
21692      */
21693     deselectRange : function(startRow, endRow, preventViewNotify){
21694         if(this.locked) return;
21695         for(var i = startRow; i <= endRow; i++){
21696             this.deselectRow(i, preventViewNotify);
21697         }
21698     },
21699
21700     /**
21701      * Selects a row.
21702      * @param {Number} row The index of the row to select
21703      * @param {Boolean} keepExisting (optional) True to keep existing selections
21704      */
21705     selectRow : function(index, keepExisting, preventViewNotify){
21706         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21707         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21708             if(!keepExisting || this.singleSelect){
21709                 this.clearSelections();
21710             }
21711             var r = this.grid.dataSource.getAt(index);
21712             this.selections.add(r);
21713             this.last = this.lastActive = index;
21714             if(!preventViewNotify){
21715                 this.grid.getView().onRowSelect(index);
21716             }
21717             this.fireEvent("rowselect", this, index, r);
21718             this.fireEvent("selectionchange", this);
21719         }
21720     },
21721
21722     /**
21723      * Deselects a row.
21724      * @param {Number} row The index of the row to deselect
21725      */
21726     deselectRow : function(index, preventViewNotify){
21727         if(this.locked) return;
21728         if(this.last == index){
21729             this.last = false;
21730         }
21731         if(this.lastActive == index){
21732             this.lastActive = false;
21733         }
21734         var r = this.grid.dataSource.getAt(index);
21735         this.selections.remove(r);
21736         if(!preventViewNotify){
21737             this.grid.getView().onRowDeselect(index);
21738         }
21739         this.fireEvent("rowdeselect", this, index);
21740         this.fireEvent("selectionchange", this);
21741     },
21742
21743     // private
21744     restoreLast : function(){
21745         if(this._last){
21746             this.last = this._last;
21747         }
21748     },
21749
21750     // private
21751     acceptsNav : function(row, col, cm){
21752         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21753     },
21754
21755     // private
21756     onEditorKey : function(field, e){
21757         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21758         if(k == e.TAB){
21759             e.stopEvent();
21760             ed.completeEdit();
21761             if(e.shiftKey){
21762                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21763             }else{
21764                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21765             }
21766         }else if(k == e.ENTER && !e.ctrlKey){
21767             e.stopEvent();
21768             ed.completeEdit();
21769             if(e.shiftKey){
21770                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21771             }else{
21772                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21773             }
21774         }else if(k == e.ESC){
21775             ed.cancelEdit();
21776         }
21777         if(newCell){
21778             g.startEditing(newCell[0], newCell[1]);
21779         }
21780     }
21781 });/*
21782  * Based on:
21783  * Ext JS Library 1.1.1
21784  * Copyright(c) 2006-2007, Ext JS, LLC.
21785  *
21786  * Originally Released Under LGPL - original licence link has changed is not relivant.
21787  *
21788  * Fork - LGPL
21789  * <script type="text/javascript">
21790  */
21791  
21792 /**
21793  * @class Roo.bootstrap.PagingToolbar
21794  * @extends Roo.bootstrap.NavSimplebar
21795  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21796  * @constructor
21797  * Create a new PagingToolbar
21798  * @param {Object} config The config object
21799  * @param {Roo.data.Store} store
21800  */
21801 Roo.bootstrap.PagingToolbar = function(config)
21802 {
21803     // old args format still supported... - xtype is prefered..
21804         // created from xtype...
21805     
21806     this.ds = config.dataSource;
21807     
21808     if (config.store && !this.ds) {
21809         this.store= Roo.factory(config.store, Roo.data);
21810         this.ds = this.store;
21811         this.ds.xmodule = this.xmodule || false;
21812     }
21813     
21814     this.toolbarItems = [];
21815     if (config.items) {
21816         this.toolbarItems = config.items;
21817     }
21818     
21819     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21820     
21821     this.cursor = 0;
21822     
21823     if (this.ds) { 
21824         this.bind(this.ds);
21825     }
21826     
21827     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21828     
21829 };
21830
21831 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21832     /**
21833      * @cfg {Roo.data.Store} dataSource
21834      * The underlying data store providing the paged data
21835      */
21836     /**
21837      * @cfg {String/HTMLElement/Element} container
21838      * container The id or element that will contain the toolbar
21839      */
21840     /**
21841      * @cfg {Boolean} displayInfo
21842      * True to display the displayMsg (defaults to false)
21843      */
21844     /**
21845      * @cfg {Number} pageSize
21846      * The number of records to display per page (defaults to 20)
21847      */
21848     pageSize: 20,
21849     /**
21850      * @cfg {String} displayMsg
21851      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21852      */
21853     displayMsg : 'Displaying {0} - {1} of {2}',
21854     /**
21855      * @cfg {String} emptyMsg
21856      * The message to display when no records are found (defaults to "No data to display")
21857      */
21858     emptyMsg : 'No data to display',
21859     /**
21860      * Customizable piece of the default paging text (defaults to "Page")
21861      * @type String
21862      */
21863     beforePageText : "Page",
21864     /**
21865      * Customizable piece of the default paging text (defaults to "of %0")
21866      * @type String
21867      */
21868     afterPageText : "of {0}",
21869     /**
21870      * Customizable piece of the default paging text (defaults to "First Page")
21871      * @type String
21872      */
21873     firstText : "First Page",
21874     /**
21875      * Customizable piece of the default paging text (defaults to "Previous Page")
21876      * @type String
21877      */
21878     prevText : "Previous Page",
21879     /**
21880      * Customizable piece of the default paging text (defaults to "Next Page")
21881      * @type String
21882      */
21883     nextText : "Next Page",
21884     /**
21885      * Customizable piece of the default paging text (defaults to "Last Page")
21886      * @type String
21887      */
21888     lastText : "Last Page",
21889     /**
21890      * Customizable piece of the default paging text (defaults to "Refresh")
21891      * @type String
21892      */
21893     refreshText : "Refresh",
21894
21895     buttons : false,
21896     // private
21897     onRender : function(ct, position) 
21898     {
21899         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21900         this.navgroup.parentId = this.id;
21901         this.navgroup.onRender(this.el, null);
21902         // add the buttons to the navgroup
21903         
21904         if(this.displayInfo){
21905             Roo.log(this.el.select('ul.navbar-nav',true).first());
21906             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21907             this.displayEl = this.el.select('.x-paging-info', true).first();
21908 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21909 //            this.displayEl = navel.el.select('span',true).first();
21910         }
21911         
21912         var _this = this;
21913         
21914         if(this.buttons){
21915             Roo.each(_this.buttons, function(e){ // this might need to use render????
21916                Roo.factory(e).onRender(_this.el, null);
21917             });
21918         }
21919             
21920         Roo.each(_this.toolbarItems, function(e) {
21921             _this.navgroup.addItem(e);
21922         });
21923         
21924         
21925         this.first = this.navgroup.addItem({
21926             tooltip: this.firstText,
21927             cls: "prev",
21928             icon : 'fa fa-backward',
21929             disabled: true,
21930             preventDefault: true,
21931             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21932         });
21933         
21934         this.prev =  this.navgroup.addItem({
21935             tooltip: this.prevText,
21936             cls: "prev",
21937             icon : 'fa fa-step-backward',
21938             disabled: true,
21939             preventDefault: true,
21940             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21941         });
21942     //this.addSeparator();
21943         
21944         
21945         var field = this.navgroup.addItem( {
21946             tagtype : 'span',
21947             cls : 'x-paging-position',
21948             
21949             html : this.beforePageText  +
21950                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21951                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21952          } ); //?? escaped?
21953         
21954         this.field = field.el.select('input', true).first();
21955         this.field.on("keydown", this.onPagingKeydown, this);
21956         this.field.on("focus", function(){this.dom.select();});
21957     
21958     
21959         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21960         //this.field.setHeight(18);
21961         //this.addSeparator();
21962         this.next = this.navgroup.addItem({
21963             tooltip: this.nextText,
21964             cls: "next",
21965             html : ' <i class="fa fa-step-forward">',
21966             disabled: true,
21967             preventDefault: true,
21968             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21969         });
21970         this.last = this.navgroup.addItem({
21971             tooltip: this.lastText,
21972             icon : 'fa fa-forward',
21973             cls: "next",
21974             disabled: true,
21975             preventDefault: true,
21976             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21977         });
21978     //this.addSeparator();
21979         this.loading = this.navgroup.addItem({
21980             tooltip: this.refreshText,
21981             icon: 'fa fa-refresh',
21982             preventDefault: true,
21983             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21984         });
21985         
21986     },
21987
21988     // private
21989     updateInfo : function(){
21990         if(this.displayEl){
21991             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21992             var msg = count == 0 ?
21993                 this.emptyMsg :
21994                 String.format(
21995                     this.displayMsg,
21996                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21997                 );
21998             this.displayEl.update(msg);
21999         }
22000     },
22001
22002     // private
22003     onLoad : function(ds, r, o){
22004        this.cursor = o.params ? o.params.start : 0;
22005        var d = this.getPageData(),
22006             ap = d.activePage,
22007             ps = d.pages;
22008         
22009        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22010        this.field.dom.value = ap;
22011        this.first.setDisabled(ap == 1);
22012        this.prev.setDisabled(ap == 1);
22013        this.next.setDisabled(ap == ps);
22014        this.last.setDisabled(ap == ps);
22015        this.loading.enable();
22016        this.updateInfo();
22017     },
22018
22019     // private
22020     getPageData : function(){
22021         var total = this.ds.getTotalCount();
22022         return {
22023             total : total,
22024             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22025             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22026         };
22027     },
22028
22029     // private
22030     onLoadError : function(){
22031         this.loading.enable();
22032     },
22033
22034     // private
22035     onPagingKeydown : function(e){
22036         var k = e.getKey();
22037         var d = this.getPageData();
22038         if(k == e.RETURN){
22039             var v = this.field.dom.value, pageNum;
22040             if(!v || isNaN(pageNum = parseInt(v, 10))){
22041                 this.field.dom.value = d.activePage;
22042                 return;
22043             }
22044             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22045             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22046             e.stopEvent();
22047         }
22048         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))
22049         {
22050           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22051           this.field.dom.value = pageNum;
22052           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22053           e.stopEvent();
22054         }
22055         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22056         {
22057           var v = this.field.dom.value, pageNum; 
22058           var increment = (e.shiftKey) ? 10 : 1;
22059           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22060             increment *= -1;
22061           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22062             this.field.dom.value = d.activePage;
22063             return;
22064           }
22065           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22066           {
22067             this.field.dom.value = parseInt(v, 10) + increment;
22068             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22069             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22070           }
22071           e.stopEvent();
22072         }
22073     },
22074
22075     // private
22076     beforeLoad : function(){
22077         if(this.loading){
22078             this.loading.disable();
22079         }
22080     },
22081
22082     // private
22083     onClick : function(which){
22084         
22085         var ds = this.ds;
22086         if (!ds) {
22087             return;
22088         }
22089         
22090         switch(which){
22091             case "first":
22092                 ds.load({params:{start: 0, limit: this.pageSize}});
22093             break;
22094             case "prev":
22095                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22096             break;
22097             case "next":
22098                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22099             break;
22100             case "last":
22101                 var total = ds.getTotalCount();
22102                 var extra = total % this.pageSize;
22103                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22104                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22105             break;
22106             case "refresh":
22107                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22108             break;
22109         }
22110     },
22111
22112     /**
22113      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22114      * @param {Roo.data.Store} store The data store to unbind
22115      */
22116     unbind : function(ds){
22117         ds.un("beforeload", this.beforeLoad, this);
22118         ds.un("load", this.onLoad, this);
22119         ds.un("loadexception", this.onLoadError, this);
22120         ds.un("remove", this.updateInfo, this);
22121         ds.un("add", this.updateInfo, this);
22122         this.ds = undefined;
22123     },
22124
22125     /**
22126      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22127      * @param {Roo.data.Store} store The data store to bind
22128      */
22129     bind : function(ds){
22130         ds.on("beforeload", this.beforeLoad, this);
22131         ds.on("load", this.onLoad, this);
22132         ds.on("loadexception", this.onLoadError, this);
22133         ds.on("remove", this.updateInfo, this);
22134         ds.on("add", this.updateInfo, this);
22135         this.ds = ds;
22136     }
22137 });/*
22138  * - LGPL
22139  *
22140  * element
22141  * 
22142  */
22143
22144 /**
22145  * @class Roo.bootstrap.MessageBar
22146  * @extends Roo.bootstrap.Component
22147  * Bootstrap MessageBar class
22148  * @cfg {String} html contents of the MessageBar
22149  * @cfg {String} weight (info | success | warning | danger) default info
22150  * @cfg {String} beforeClass insert the bar before the given class
22151  * @cfg {Boolean} closable (true | false) default false
22152  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22153  * 
22154  * @constructor
22155  * Create a new Element
22156  * @param {Object} config The config object
22157  */
22158
22159 Roo.bootstrap.MessageBar = function(config){
22160     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22161 };
22162
22163 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22164     
22165     html: '',
22166     weight: 'info',
22167     closable: false,
22168     fixed: false,
22169     beforeClass: 'bootstrap-sticky-wrap',
22170     
22171     getAutoCreate : function(){
22172         
22173         var cfg = {
22174             tag: 'div',
22175             cls: 'alert alert-dismissable alert-' + this.weight,
22176             cn: [
22177                 {
22178                     tag: 'span',
22179                     cls: 'message',
22180                     html: this.html || ''
22181                 }
22182             ]
22183         }
22184         
22185         if(this.fixed){
22186             cfg.cls += ' alert-messages-fixed';
22187         }
22188         
22189         if(this.closable){
22190             cfg.cn.push({
22191                 tag: 'button',
22192                 cls: 'close',
22193                 html: 'x'
22194             });
22195         }
22196         
22197         return cfg;
22198     },
22199     
22200     onRender : function(ct, position)
22201     {
22202         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22203         
22204         if(!this.el){
22205             var cfg = Roo.apply({},  this.getAutoCreate());
22206             cfg.id = Roo.id();
22207             
22208             if (this.cls) {
22209                 cfg.cls += ' ' + this.cls;
22210             }
22211             if (this.style) {
22212                 cfg.style = this.style;
22213             }
22214             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22215             
22216             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22217         }
22218         
22219         this.el.select('>button.close').on('click', this.hide, this);
22220         
22221     },
22222     
22223     show : function()
22224     {
22225         if (!this.rendered) {
22226             this.render();
22227         }
22228         
22229         this.el.show();
22230         
22231         this.fireEvent('show', this);
22232         
22233     },
22234     
22235     hide : function()
22236     {
22237         if (!this.rendered) {
22238             this.render();
22239         }
22240         
22241         this.el.hide();
22242         
22243         this.fireEvent('hide', this);
22244     },
22245     
22246     update : function()
22247     {
22248 //        var e = this.el.dom.firstChild;
22249 //        
22250 //        if(this.closable){
22251 //            e = e.nextSibling;
22252 //        }
22253 //        
22254 //        e.data = this.html || '';
22255
22256         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22257     }
22258    
22259 });
22260
22261  
22262
22263      /*
22264  * - LGPL
22265  *
22266  * Graph
22267  * 
22268  */
22269
22270
22271 /**
22272  * @class Roo.bootstrap.Graph
22273  * @extends Roo.bootstrap.Component
22274  * Bootstrap Graph class
22275 > Prameters
22276  -sm {number} sm 4
22277  -md {number} md 5
22278  @cfg {String} graphtype  bar | vbar | pie
22279  @cfg {number} g_x coodinator | centre x (pie)
22280  @cfg {number} g_y coodinator | centre y (pie)
22281  @cfg {number} g_r radius (pie)
22282  @cfg {number} g_height height of the chart (respected by all elements in the set)
22283  @cfg {number} g_width width of the chart (respected by all elements in the set)
22284  @cfg {Object} title The title of the chart
22285     
22286  -{Array}  values
22287  -opts (object) options for the chart 
22288      o {
22289      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22290      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22291      o vgutter (number)
22292      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.
22293      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22294      o to
22295      o stretch (boolean)
22296      o }
22297  -opts (object) options for the pie
22298      o{
22299      o cut
22300      o startAngle (number)
22301      o endAngle (number)
22302      } 
22303  *
22304  * @constructor
22305  * Create a new Input
22306  * @param {Object} config The config object
22307  */
22308
22309 Roo.bootstrap.Graph = function(config){
22310     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22311     
22312     this.addEvents({
22313         // img events
22314         /**
22315          * @event click
22316          * The img click event for the img.
22317          * @param {Roo.EventObject} e
22318          */
22319         "click" : true
22320     });
22321 };
22322
22323 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22324     
22325     sm: 4,
22326     md: 5,
22327     graphtype: 'bar',
22328     g_height: 250,
22329     g_width: 400,
22330     g_x: 50,
22331     g_y: 50,
22332     g_r: 30,
22333     opts:{
22334         //g_colors: this.colors,
22335         g_type: 'soft',
22336         g_gutter: '20%'
22337
22338     },
22339     title : false,
22340
22341     getAutoCreate : function(){
22342         
22343         var cfg = {
22344             tag: 'div',
22345             html : null
22346         }
22347         
22348         
22349         return  cfg;
22350     },
22351
22352     onRender : function(ct,position){
22353         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22354         this.raphael = Raphael(this.el.dom);
22355         
22356                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22357                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22358                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22359                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22360                 /*
22361                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22362                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22363                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22364                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22365                 
22366                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22367                 r.barchart(330, 10, 300, 220, data1);
22368                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22369                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22370                 */
22371                 
22372                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22373                 // r.barchart(30, 30, 560, 250,  xdata, {
22374                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22375                 //     axis : "0 0 1 1",
22376                 //     axisxlabels :  xdata
22377                 //     //yvalues : cols,
22378                    
22379                 // });
22380 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22381 //        
22382 //        this.load(null,xdata,{
22383 //                axis : "0 0 1 1",
22384 //                axisxlabels :  xdata
22385 //                });
22386
22387     },
22388
22389     load : function(graphtype,xdata,opts){
22390         this.raphael.clear();
22391         if(!graphtype) {
22392             graphtype = this.graphtype;
22393         }
22394         if(!opts){
22395             opts = this.opts;
22396         }
22397         var r = this.raphael,
22398             fin = function () {
22399                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22400             },
22401             fout = function () {
22402                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22403             },
22404             pfin = function() {
22405                 this.sector.stop();
22406                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22407
22408                 if (this.label) {
22409                     this.label[0].stop();
22410                     this.label[0].attr({ r: 7.5 });
22411                     this.label[1].attr({ "font-weight": 800 });
22412                 }
22413             },
22414             pfout = function() {
22415                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22416
22417                 if (this.label) {
22418                     this.label[0].animate({ r: 5 }, 500, "bounce");
22419                     this.label[1].attr({ "font-weight": 400 });
22420                 }
22421             };
22422
22423         switch(graphtype){
22424             case 'bar':
22425                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22426                 break;
22427             case 'hbar':
22428                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22429                 break;
22430             case 'pie':
22431 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22432 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22433 //            
22434                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22435                 
22436                 break;
22437
22438         }
22439         
22440         if(this.title){
22441             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22442         }
22443         
22444     },
22445     
22446     setTitle: function(o)
22447     {
22448         this.title = o;
22449     },
22450     
22451     initEvents: function() {
22452         
22453         if(!this.href){
22454             this.el.on('click', this.onClick, this);
22455         }
22456     },
22457     
22458     onClick : function(e)
22459     {
22460         Roo.log('img onclick');
22461         this.fireEvent('click', this, e);
22462     }
22463    
22464 });
22465
22466  
22467 /*
22468  * - LGPL
22469  *
22470  * numberBox
22471  * 
22472  */
22473 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22474
22475 /**
22476  * @class Roo.bootstrap.dash.NumberBox
22477  * @extends Roo.bootstrap.Component
22478  * Bootstrap NumberBox class
22479  * @cfg {String} headline Box headline
22480  * @cfg {String} content Box content
22481  * @cfg {String} icon Box icon
22482  * @cfg {String} footer Footer text
22483  * @cfg {String} fhref Footer href
22484  * 
22485  * @constructor
22486  * Create a new NumberBox
22487  * @param {Object} config The config object
22488  */
22489
22490
22491 Roo.bootstrap.dash.NumberBox = function(config){
22492     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22493     
22494 };
22495
22496 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22497     
22498     headline : '',
22499     content : '',
22500     icon : '',
22501     footer : '',
22502     fhref : '',
22503     ficon : '',
22504     
22505     getAutoCreate : function(){
22506         
22507         var cfg = {
22508             tag : 'div',
22509             cls : 'small-box ',
22510             cn : [
22511                 {
22512                     tag : 'div',
22513                     cls : 'inner',
22514                     cn :[
22515                         {
22516                             tag : 'h3',
22517                             cls : 'roo-headline',
22518                             html : this.headline
22519                         },
22520                         {
22521                             tag : 'p',
22522                             cls : 'roo-content',
22523                             html : this.content
22524                         }
22525                     ]
22526                 }
22527             ]
22528         }
22529         
22530         if(this.icon){
22531             cfg.cn.push({
22532                 tag : 'div',
22533                 cls : 'icon',
22534                 cn :[
22535                     {
22536                         tag : 'i',
22537                         cls : 'ion ' + this.icon
22538                     }
22539                 ]
22540             });
22541         }
22542         
22543         if(this.footer){
22544             var footer = {
22545                 tag : 'a',
22546                 cls : 'small-box-footer',
22547                 href : this.fhref || '#',
22548                 html : this.footer
22549             };
22550             
22551             cfg.cn.push(footer);
22552             
22553         }
22554         
22555         return  cfg;
22556     },
22557
22558     onRender : function(ct,position){
22559         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22560
22561
22562        
22563                 
22564     },
22565
22566     setHeadline: function (value)
22567     {
22568         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22569     },
22570     
22571     setFooter: function (value, href)
22572     {
22573         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22574         
22575         if(href){
22576             this.el.select('a.small-box-footer',true).first().attr('href', href);
22577         }
22578         
22579     },
22580
22581     setContent: function (value)
22582     {
22583         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22584     },
22585
22586     initEvents: function() 
22587     {   
22588         
22589     }
22590     
22591 });
22592
22593  
22594 /*
22595  * - LGPL
22596  *
22597  * TabBox
22598  * 
22599  */
22600 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22601
22602 /**
22603  * @class Roo.bootstrap.dash.TabBox
22604  * @extends Roo.bootstrap.Component
22605  * Bootstrap TabBox class
22606  * @cfg {String} title Title of the TabBox
22607  * @cfg {String} icon Icon of the TabBox
22608  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22609  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22610  * 
22611  * @constructor
22612  * Create a new TabBox
22613  * @param {Object} config The config object
22614  */
22615
22616
22617 Roo.bootstrap.dash.TabBox = function(config){
22618     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22619     this.addEvents({
22620         // raw events
22621         /**
22622          * @event addpane
22623          * When a pane is added
22624          * @param {Roo.bootstrap.dash.TabPane} pane
22625          */
22626         "addpane" : true,
22627         /**
22628          * @event activatepane
22629          * When a pane is activated
22630          * @param {Roo.bootstrap.dash.TabPane} pane
22631          */
22632         "activatepane" : true
22633         
22634          
22635     });
22636     
22637     this.panes = [];
22638 };
22639
22640 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22641
22642     title : '',
22643     icon : false,
22644     showtabs : true,
22645     tabScrollable : false,
22646     
22647     getChildContainer : function()
22648     {
22649         return this.el.select('.tab-content', true).first();
22650     },
22651     
22652     getAutoCreate : function(){
22653         
22654         var header = {
22655             tag: 'li',
22656             cls: 'pull-left header',
22657             html: this.title,
22658             cn : []
22659         };
22660         
22661         if(this.icon){
22662             header.cn.push({
22663                 tag: 'i',
22664                 cls: 'fa ' + this.icon
22665             });
22666         }
22667         
22668         var h = {
22669             tag: 'ul',
22670             cls: 'nav nav-tabs pull-right',
22671             cn: [
22672                 header
22673             ]
22674         };
22675         
22676         if(this.tabScrollable){
22677             h = {
22678                 tag: 'div',
22679                 cls: 'tab-header',
22680                 cn: [
22681                     {
22682                         tag: 'ul',
22683                         cls: 'nav nav-tabs pull-right',
22684                         cn: [
22685                             header
22686                         ]
22687                     }
22688                 ]
22689             }
22690         }
22691         
22692         var cfg = {
22693             tag: 'div',
22694             cls: 'nav-tabs-custom',
22695             cn: [
22696                 h,
22697                 {
22698                     tag: 'div',
22699                     cls: 'tab-content no-padding',
22700                     cn: []
22701                 }
22702             ]
22703         }
22704
22705         return  cfg;
22706     },
22707     initEvents : function()
22708     {
22709         //Roo.log('add add pane handler');
22710         this.on('addpane', this.onAddPane, this);
22711     },
22712      /**
22713      * Updates the box title
22714      * @param {String} html to set the title to.
22715      */
22716     setTitle : function(value)
22717     {
22718         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22719     },
22720     onAddPane : function(pane)
22721     {
22722         this.panes.push(pane);
22723         //Roo.log('addpane');
22724         //Roo.log(pane);
22725         // tabs are rendere left to right..
22726         if(!this.showtabs){
22727             return;
22728         }
22729         
22730         var ctr = this.el.select('.nav-tabs', true).first();
22731          
22732          
22733         var existing = ctr.select('.nav-tab',true);
22734         var qty = existing.getCount();;
22735         
22736         
22737         var tab = ctr.createChild({
22738             tag : 'li',
22739             cls : 'nav-tab' + (qty ? '' : ' active'),
22740             cn : [
22741                 {
22742                     tag : 'a',
22743                     href:'#',
22744                     html : pane.title
22745                 }
22746             ]
22747         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22748         pane.tab = tab;
22749         
22750         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22751         if (!qty) {
22752             pane.el.addClass('active');
22753         }
22754         
22755                 
22756     },
22757     onTabClick : function(ev,un,ob,pane)
22758     {
22759         //Roo.log('tab - prev default');
22760         ev.preventDefault();
22761         
22762         
22763         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22764         pane.tab.addClass('active');
22765         //Roo.log(pane.title);
22766         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22767         // technically we should have a deactivate event.. but maybe add later.
22768         // and it should not de-activate the selected tab...
22769         this.fireEvent('activatepane', pane);
22770         pane.el.addClass('active');
22771         pane.fireEvent('activate');
22772         
22773         
22774     },
22775     
22776     getActivePane : function()
22777     {
22778         var r = false;
22779         Roo.each(this.panes, function(p) {
22780             if(p.el.hasClass('active')){
22781                 r = p;
22782                 return false;
22783             }
22784             
22785             return;
22786         });
22787         
22788         return r;
22789     }
22790     
22791     
22792 });
22793
22794  
22795 /*
22796  * - LGPL
22797  *
22798  * Tab pane
22799  * 
22800  */
22801 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22802 /**
22803  * @class Roo.bootstrap.TabPane
22804  * @extends Roo.bootstrap.Component
22805  * Bootstrap TabPane class
22806  * @cfg {Boolean} active (false | true) Default false
22807  * @cfg {String} title title of panel
22808
22809  * 
22810  * @constructor
22811  * Create a new TabPane
22812  * @param {Object} config The config object
22813  */
22814
22815 Roo.bootstrap.dash.TabPane = function(config){
22816     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22817     
22818     this.addEvents({
22819         // raw events
22820         /**
22821          * @event activate
22822          * When a pane is activated
22823          * @param {Roo.bootstrap.dash.TabPane} pane
22824          */
22825         "activate" : true
22826          
22827     });
22828 };
22829
22830 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22831     
22832     active : false,
22833     title : '',
22834     
22835     // the tabBox that this is attached to.
22836     tab : false,
22837      
22838     getAutoCreate : function() 
22839     {
22840         var cfg = {
22841             tag: 'div',
22842             cls: 'tab-pane'
22843         }
22844         
22845         if(this.active){
22846             cfg.cls += ' active';
22847         }
22848         
22849         return cfg;
22850     },
22851     initEvents  : function()
22852     {
22853         //Roo.log('trigger add pane handler');
22854         this.parent().fireEvent('addpane', this)
22855     },
22856     
22857      /**
22858      * Updates the tab title 
22859      * @param {String} html to set the title to.
22860      */
22861     setTitle: function(str)
22862     {
22863         if (!this.tab) {
22864             return;
22865         }
22866         this.title = str;
22867         this.tab.select('a', true).first().dom.innerHTML = str;
22868         
22869     }
22870     
22871     
22872     
22873 });
22874
22875  
22876
22877
22878  /*
22879  * - LGPL
22880  *
22881  * menu
22882  * 
22883  */
22884 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22885
22886 /**
22887  * @class Roo.bootstrap.menu.Menu
22888  * @extends Roo.bootstrap.Component
22889  * Bootstrap Menu class - container for Menu
22890  * @cfg {String} html Text of the menu
22891  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22892  * @cfg {String} icon Font awesome icon
22893  * @cfg {String} pos Menu align to (top | bottom) default bottom
22894  * 
22895  * 
22896  * @constructor
22897  * Create a new Menu
22898  * @param {Object} config The config object
22899  */
22900
22901
22902 Roo.bootstrap.menu.Menu = function(config){
22903     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22904     
22905     this.addEvents({
22906         /**
22907          * @event beforeshow
22908          * Fires before this menu is displayed
22909          * @param {Roo.bootstrap.menu.Menu} this
22910          */
22911         beforeshow : true,
22912         /**
22913          * @event beforehide
22914          * Fires before this menu is hidden
22915          * @param {Roo.bootstrap.menu.Menu} this
22916          */
22917         beforehide : true,
22918         /**
22919          * @event show
22920          * Fires after this menu is displayed
22921          * @param {Roo.bootstrap.menu.Menu} this
22922          */
22923         show : true,
22924         /**
22925          * @event hide
22926          * Fires after this menu is hidden
22927          * @param {Roo.bootstrap.menu.Menu} this
22928          */
22929         hide : true,
22930         /**
22931          * @event click
22932          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22933          * @param {Roo.bootstrap.menu.Menu} this
22934          * @param {Roo.EventObject} e
22935          */
22936         click : true
22937     });
22938     
22939 };
22940
22941 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22942     
22943     submenu : false,
22944     html : '',
22945     weight : 'default',
22946     icon : false,
22947     pos : 'bottom',
22948     
22949     
22950     getChildContainer : function() {
22951         if(this.isSubMenu){
22952             return this.el;
22953         }
22954         
22955         return this.el.select('ul.dropdown-menu', true).first();  
22956     },
22957     
22958     getAutoCreate : function()
22959     {
22960         var text = [
22961             {
22962                 tag : 'span',
22963                 cls : 'roo-menu-text',
22964                 html : this.html
22965             }
22966         ];
22967         
22968         if(this.icon){
22969             text.unshift({
22970                 tag : 'i',
22971                 cls : 'fa ' + this.icon
22972             })
22973         }
22974         
22975         
22976         var cfg = {
22977             tag : 'div',
22978             cls : 'btn-group',
22979             cn : [
22980                 {
22981                     tag : 'button',
22982                     cls : 'dropdown-button btn btn-' + this.weight,
22983                     cn : text
22984                 },
22985                 {
22986                     tag : 'button',
22987                     cls : 'dropdown-toggle btn btn-' + this.weight,
22988                     cn : [
22989                         {
22990                             tag : 'span',
22991                             cls : 'caret'
22992                         }
22993                     ]
22994                 },
22995                 {
22996                     tag : 'ul',
22997                     cls : 'dropdown-menu'
22998                 }
22999             ]
23000             
23001         };
23002         
23003         if(this.pos == 'top'){
23004             cfg.cls += ' dropup';
23005         }
23006         
23007         if(this.isSubMenu){
23008             cfg = {
23009                 tag : 'ul',
23010                 cls : 'dropdown-menu'
23011             }
23012         }
23013         
23014         return cfg;
23015     },
23016     
23017     onRender : function(ct, position)
23018     {
23019         this.isSubMenu = ct.hasClass('dropdown-submenu');
23020         
23021         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23022     },
23023     
23024     initEvents : function() 
23025     {
23026         if(this.isSubMenu){
23027             return;
23028         }
23029         
23030         this.hidden = true;
23031         
23032         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23033         this.triggerEl.on('click', this.onTriggerPress, this);
23034         
23035         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23036         this.buttonEl.on('click', this.onClick, this);
23037         
23038     },
23039     
23040     list : function()
23041     {
23042         if(this.isSubMenu){
23043             return this.el;
23044         }
23045         
23046         return this.el.select('ul.dropdown-menu', true).first();
23047     },
23048     
23049     onClick : function(e)
23050     {
23051         this.fireEvent("click", this, e);
23052     },
23053     
23054     onTriggerPress  : function(e)
23055     {   
23056         if (this.isVisible()) {
23057             this.hide();
23058         } else {
23059             this.show();
23060         }
23061     },
23062     
23063     isVisible : function(){
23064         return !this.hidden;
23065     },
23066     
23067     show : function()
23068     {
23069         this.fireEvent("beforeshow", this);
23070         
23071         this.hidden = false;
23072         this.el.addClass('open');
23073         
23074         Roo.get(document).on("mouseup", this.onMouseUp, this);
23075         
23076         this.fireEvent("show", this);
23077         
23078         
23079     },
23080     
23081     hide : function()
23082     {
23083         this.fireEvent("beforehide", this);
23084         
23085         this.hidden = true;
23086         this.el.removeClass('open');
23087         
23088         Roo.get(document).un("mouseup", this.onMouseUp);
23089         
23090         this.fireEvent("hide", this);
23091     },
23092     
23093     onMouseUp : function()
23094     {
23095         this.hide();
23096     }
23097     
23098 });
23099
23100  
23101  /*
23102  * - LGPL
23103  *
23104  * menu item
23105  * 
23106  */
23107 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23108
23109 /**
23110  * @class Roo.bootstrap.menu.Item
23111  * @extends Roo.bootstrap.Component
23112  * Bootstrap MenuItem class
23113  * @cfg {Boolean} submenu (true | false) default false
23114  * @cfg {String} html text of the item
23115  * @cfg {String} href the link
23116  * @cfg {Boolean} disable (true | false) default false
23117  * @cfg {Boolean} preventDefault (true | false) default true
23118  * @cfg {String} icon Font awesome icon
23119  * @cfg {String} pos Submenu align to (left | right) default right 
23120  * 
23121  * 
23122  * @constructor
23123  * Create a new Item
23124  * @param {Object} config The config object
23125  */
23126
23127
23128 Roo.bootstrap.menu.Item = function(config){
23129     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23130     this.addEvents({
23131         /**
23132          * @event mouseover
23133          * Fires when the mouse is hovering over this menu
23134          * @param {Roo.bootstrap.menu.Item} this
23135          * @param {Roo.EventObject} e
23136          */
23137         mouseover : true,
23138         /**
23139          * @event mouseout
23140          * Fires when the mouse exits this menu
23141          * @param {Roo.bootstrap.menu.Item} this
23142          * @param {Roo.EventObject} e
23143          */
23144         mouseout : true,
23145         // raw events
23146         /**
23147          * @event click
23148          * The raw click event for the entire grid.
23149          * @param {Roo.EventObject} e
23150          */
23151         click : true
23152     });
23153 };
23154
23155 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23156     
23157     submenu : false,
23158     href : '',
23159     html : '',
23160     preventDefault: true,
23161     disable : false,
23162     icon : false,
23163     pos : 'right',
23164     
23165     getAutoCreate : function()
23166     {
23167         var text = [
23168             {
23169                 tag : 'span',
23170                 cls : 'roo-menu-item-text',
23171                 html : this.html
23172             }
23173         ];
23174         
23175         if(this.icon){
23176             text.unshift({
23177                 tag : 'i',
23178                 cls : 'fa ' + this.icon
23179             })
23180         }
23181         
23182         var cfg = {
23183             tag : 'li',
23184             cn : [
23185                 {
23186                     tag : 'a',
23187                     href : this.href || '#',
23188                     cn : text
23189                 }
23190             ]
23191         };
23192         
23193         if(this.disable){
23194             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23195         }
23196         
23197         if(this.submenu){
23198             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23199             
23200             if(this.pos == 'left'){
23201                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23202             }
23203         }
23204         
23205         return cfg;
23206     },
23207     
23208     initEvents : function() 
23209     {
23210         this.el.on('mouseover', this.onMouseOver, this);
23211         this.el.on('mouseout', this.onMouseOut, this);
23212         
23213         this.el.select('a', true).first().on('click', this.onClick, this);
23214         
23215     },
23216     
23217     onClick : function(e)
23218     {
23219         if(this.preventDefault){
23220             e.preventDefault();
23221         }
23222         
23223         this.fireEvent("click", this, e);
23224     },
23225     
23226     onMouseOver : function(e)
23227     {
23228         if(this.submenu && this.pos == 'left'){
23229             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23230         }
23231         
23232         this.fireEvent("mouseover", this, e);
23233     },
23234     
23235     onMouseOut : function(e)
23236     {
23237         this.fireEvent("mouseout", this, e);
23238     }
23239 });
23240
23241  
23242
23243  /*
23244  * - LGPL
23245  *
23246  * menu separator
23247  * 
23248  */
23249 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23250
23251 /**
23252  * @class Roo.bootstrap.menu.Separator
23253  * @extends Roo.bootstrap.Component
23254  * Bootstrap Separator class
23255  * 
23256  * @constructor
23257  * Create a new Separator
23258  * @param {Object} config The config object
23259  */
23260
23261
23262 Roo.bootstrap.menu.Separator = function(config){
23263     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23264 };
23265
23266 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23267     
23268     getAutoCreate : function(){
23269         var cfg = {
23270             tag : 'li',
23271             cls: 'divider'
23272         };
23273         
23274         return cfg;
23275     }
23276    
23277 });
23278
23279  
23280
23281  /*
23282  * - LGPL
23283  *
23284  * Tooltip
23285  * 
23286  */
23287
23288 /**
23289  * @class Roo.bootstrap.Tooltip
23290  * Bootstrap Tooltip class
23291  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23292  * to determine which dom element triggers the tooltip.
23293  * 
23294  * It needs to add support for additional attributes like tooltip-position
23295  * 
23296  * @constructor
23297  * Create a new Toolti
23298  * @param {Object} config The config object
23299  */
23300
23301 Roo.bootstrap.Tooltip = function(config){
23302     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23303 };
23304
23305 Roo.apply(Roo.bootstrap.Tooltip, {
23306     /**
23307      * @function init initialize tooltip monitoring.
23308      * @static
23309      */
23310     currentEl : false,
23311     currentTip : false,
23312     currentRegion : false,
23313     
23314     //  init : delay?
23315     
23316     init : function()
23317     {
23318         Roo.get(document).on('mouseover', this.enter ,this);
23319         Roo.get(document).on('mouseout', this.leave, this);
23320          
23321         
23322         this.currentTip = new Roo.bootstrap.Tooltip();
23323     },
23324     
23325     enter : function(ev)
23326     {
23327         var dom = ev.getTarget();
23328         
23329         //Roo.log(['enter',dom]);
23330         var el = Roo.fly(dom);
23331         if (this.currentEl) {
23332             //Roo.log(dom);
23333             //Roo.log(this.currentEl);
23334             //Roo.log(this.currentEl.contains(dom));
23335             if (this.currentEl == el) {
23336                 return;
23337             }
23338             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23339                 return;
23340             }
23341
23342         }
23343         
23344         if (this.currentTip.el) {
23345             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23346         }    
23347         //Roo.log(ev);
23348         var bindEl = el;
23349         
23350         // you can not look for children, as if el is the body.. then everythign is the child..
23351         if (!el.attr('tooltip')) { //
23352             if (!el.select("[tooltip]").elements.length) {
23353                 return;
23354             }
23355             // is the mouse over this child...?
23356             bindEl = el.select("[tooltip]").first();
23357             var xy = ev.getXY();
23358             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23359                 //Roo.log("not in region.");
23360                 return;
23361             }
23362             //Roo.log("child element over..");
23363             
23364         }
23365         this.currentEl = bindEl;
23366         this.currentTip.bind(bindEl);
23367         this.currentRegion = Roo.lib.Region.getRegion(dom);
23368         this.currentTip.enter();
23369         
23370     },
23371     leave : function(ev)
23372     {
23373         var dom = ev.getTarget();
23374         //Roo.log(['leave',dom]);
23375         if (!this.currentEl) {
23376             return;
23377         }
23378         
23379         
23380         if (dom != this.currentEl.dom) {
23381             return;
23382         }
23383         var xy = ev.getXY();
23384         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23385             return;
23386         }
23387         // only activate leave if mouse cursor is outside... bounding box..
23388         
23389         
23390         
23391         
23392         if (this.currentTip) {
23393             this.currentTip.leave();
23394         }
23395         //Roo.log('clear currentEl');
23396         this.currentEl = false;
23397         
23398         
23399     },
23400     alignment : {
23401         'left' : ['r-l', [-2,0], 'right'],
23402         'right' : ['l-r', [2,0], 'left'],
23403         'bottom' : ['t-b', [0,2], 'top'],
23404         'top' : [ 'b-t', [0,-2], 'bottom']
23405     }
23406     
23407 });
23408
23409
23410 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23411     
23412     
23413     bindEl : false,
23414     
23415     delay : null, // can be { show : 300 , hide: 500}
23416     
23417     timeout : null,
23418     
23419     hoverState : null, //???
23420     
23421     placement : 'bottom', 
23422     
23423     getAutoCreate : function(){
23424     
23425         var cfg = {
23426            cls : 'tooltip',
23427            role : 'tooltip',
23428            cn : [
23429                 {
23430                     cls : 'tooltip-arrow'
23431                 },
23432                 {
23433                     cls : 'tooltip-inner'
23434                 }
23435            ]
23436         };
23437         
23438         return cfg;
23439     },
23440     bind : function(el)
23441     {
23442         this.bindEl = el;
23443     },
23444       
23445     
23446     enter : function () {
23447        
23448         if (this.timeout != null) {
23449             clearTimeout(this.timeout);
23450         }
23451         
23452         this.hoverState = 'in';
23453          //Roo.log("enter - show");
23454         if (!this.delay || !this.delay.show) {
23455             this.show();
23456             return;
23457         }
23458         var _t = this;
23459         this.timeout = setTimeout(function () {
23460             if (_t.hoverState == 'in') {
23461                 _t.show();
23462             }
23463         }, this.delay.show);
23464     },
23465     leave : function()
23466     {
23467         clearTimeout(this.timeout);
23468     
23469         this.hoverState = 'out';
23470          if (!this.delay || !this.delay.hide) {
23471             this.hide();
23472             return;
23473         }
23474        
23475         var _t = this;
23476         this.timeout = setTimeout(function () {
23477             //Roo.log("leave - timeout");
23478             
23479             if (_t.hoverState == 'out') {
23480                 _t.hide();
23481                 Roo.bootstrap.Tooltip.currentEl = false;
23482             }
23483         }, delay);
23484     },
23485     
23486     show : function ()
23487     {
23488         if (!this.el) {
23489             this.render(document.body);
23490         }
23491         // set content.
23492         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23493         
23494         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23495         
23496         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23497         
23498         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23499         
23500         var placement = typeof this.placement == 'function' ?
23501             this.placement.call(this, this.el, on_el) :
23502             this.placement;
23503             
23504         var autoToken = /\s?auto?\s?/i;
23505         var autoPlace = autoToken.test(placement);
23506         if (autoPlace) {
23507             placement = placement.replace(autoToken, '') || 'top';
23508         }
23509         
23510         //this.el.detach()
23511         //this.el.setXY([0,0]);
23512         this.el.show();
23513         //this.el.dom.style.display='block';
23514         
23515         //this.el.appendTo(on_el);
23516         
23517         var p = this.getPosition();
23518         var box = this.el.getBox();
23519         
23520         if (autoPlace) {
23521             // fixme..
23522         }
23523         
23524         var align = Roo.bootstrap.Tooltip.alignment[placement];
23525         
23526         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23527         
23528         if(placement == 'top' || placement == 'bottom'){
23529             if(xy[0] < 0){
23530                 placement = 'right';
23531             }
23532             
23533             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23534                 placement = 'left';
23535             }
23536         }
23537         
23538         align = Roo.bootstrap.Tooltip.alignment[placement];
23539         
23540         this.el.alignTo(this.bindEl, align[0],align[1]);
23541         //var arrow = this.el.select('.arrow',true).first();
23542         //arrow.set(align[2], 
23543         
23544         this.el.addClass(placement);
23545         
23546         this.el.addClass('in fade');
23547         
23548         this.hoverState = null;
23549         
23550         if (this.el.hasClass('fade')) {
23551             // fade it?
23552         }
23553         
23554     },
23555     hide : function()
23556     {
23557          
23558         if (!this.el) {
23559             return;
23560         }
23561         //this.el.setXY([0,0]);
23562         this.el.removeClass('in');
23563         //this.el.hide();
23564         
23565     }
23566     
23567 });
23568  
23569
23570  /*
23571  * - LGPL
23572  *
23573  * Location Picker
23574  * 
23575  */
23576
23577 /**
23578  * @class Roo.bootstrap.LocationPicker
23579  * @extends Roo.bootstrap.Component
23580  * Bootstrap LocationPicker class
23581  * @cfg {Number} latitude Position when init default 0
23582  * @cfg {Number} longitude Position when init default 0
23583  * @cfg {Number} zoom default 15
23584  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23585  * @cfg {Boolean} mapTypeControl default false
23586  * @cfg {Boolean} disableDoubleClickZoom default false
23587  * @cfg {Boolean} scrollwheel default true
23588  * @cfg {Boolean} streetViewControl default false
23589  * @cfg {Number} radius default 0
23590  * @cfg {String} locationName
23591  * @cfg {Boolean} draggable default true
23592  * @cfg {Boolean} enableAutocomplete default false
23593  * @cfg {Boolean} enableReverseGeocode default true
23594  * @cfg {String} markerTitle
23595  * 
23596  * @constructor
23597  * Create a new LocationPicker
23598  * @param {Object} config The config object
23599  */
23600
23601
23602 Roo.bootstrap.LocationPicker = function(config){
23603     
23604     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23605     
23606     this.addEvents({
23607         /**
23608          * @event initial
23609          * Fires when the picker initialized.
23610          * @param {Roo.bootstrap.LocationPicker} this
23611          * @param {Google Location} location
23612          */
23613         initial : true,
23614         /**
23615          * @event positionchanged
23616          * Fires when the picker position changed.
23617          * @param {Roo.bootstrap.LocationPicker} this
23618          * @param {Google Location} location
23619          */
23620         positionchanged : true,
23621         /**
23622          * @event resize
23623          * Fires when the map resize.
23624          * @param {Roo.bootstrap.LocationPicker} this
23625          */
23626         resize : true,
23627         /**
23628          * @event show
23629          * Fires when the map show.
23630          * @param {Roo.bootstrap.LocationPicker} this
23631          */
23632         show : true,
23633         /**
23634          * @event hide
23635          * Fires when the map hide.
23636          * @param {Roo.bootstrap.LocationPicker} this
23637          */
23638         hide : true,
23639         /**
23640          * @event mapClick
23641          * Fires when click the map.
23642          * @param {Roo.bootstrap.LocationPicker} this
23643          * @param {Map event} e
23644          */
23645         mapClick : true,
23646         /**
23647          * @event mapRightClick
23648          * Fires when right click the map.
23649          * @param {Roo.bootstrap.LocationPicker} this
23650          * @param {Map event} e
23651          */
23652         mapRightClick : true,
23653         /**
23654          * @event markerClick
23655          * Fires when click the marker.
23656          * @param {Roo.bootstrap.LocationPicker} this
23657          * @param {Map event} e
23658          */
23659         markerClick : true,
23660         /**
23661          * @event markerRightClick
23662          * Fires when right click the marker.
23663          * @param {Roo.bootstrap.LocationPicker} this
23664          * @param {Map event} e
23665          */
23666         markerRightClick : true,
23667         /**
23668          * @event OverlayViewDraw
23669          * Fires when OverlayView Draw
23670          * @param {Roo.bootstrap.LocationPicker} this
23671          */
23672         OverlayViewDraw : true,
23673         /**
23674          * @event OverlayViewOnAdd
23675          * Fires when OverlayView Draw
23676          * @param {Roo.bootstrap.LocationPicker} this
23677          */
23678         OverlayViewOnAdd : true,
23679         /**
23680          * @event OverlayViewOnRemove
23681          * Fires when OverlayView Draw
23682          * @param {Roo.bootstrap.LocationPicker} this
23683          */
23684         OverlayViewOnRemove : true,
23685         /**
23686          * @event OverlayViewShow
23687          * Fires when OverlayView Draw
23688          * @param {Roo.bootstrap.LocationPicker} this
23689          * @param {Pixel} cpx
23690          */
23691         OverlayViewShow : true,
23692         /**
23693          * @event OverlayViewHide
23694          * Fires when OverlayView Draw
23695          * @param {Roo.bootstrap.LocationPicker} this
23696          */
23697         OverlayViewHide : true,
23698         /**
23699          * @event loadexception
23700          * Fires when load google lib failed.
23701          * @param {Roo.bootstrap.LocationPicker} this
23702          */
23703         loadexception : true
23704     });
23705         
23706 };
23707
23708 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23709     
23710     gMapContext: false,
23711     
23712     latitude: 0,
23713     longitude: 0,
23714     zoom: 15,
23715     mapTypeId: false,
23716     mapTypeControl: false,
23717     disableDoubleClickZoom: false,
23718     scrollwheel: true,
23719     streetViewControl: false,
23720     radius: 0,
23721     locationName: '',
23722     draggable: true,
23723     enableAutocomplete: false,
23724     enableReverseGeocode: true,
23725     markerTitle: '',
23726     
23727     getAutoCreate: function()
23728     {
23729
23730         var cfg = {
23731             tag: 'div',
23732             cls: 'roo-location-picker'
23733         };
23734         
23735         return cfg
23736     },
23737     
23738     initEvents: function(ct, position)
23739     {       
23740         if(!this.el.getWidth() || this.isApplied()){
23741             return;
23742         }
23743         
23744         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23745         
23746         this.initial();
23747     },
23748     
23749     initial: function()
23750     {
23751         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
23752             this.fireEvent('loadexception', this);
23753             return;
23754         }
23755         
23756         if(!this.mapTypeId){
23757             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23758         }
23759         
23760         this.gMapContext = this.GMapContext();
23761         
23762         this.initOverlayView();
23763         
23764         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23765         
23766         var _this = this;
23767                 
23768         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23769             _this.setPosition(_this.gMapContext.marker.position);
23770         });
23771         
23772         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23773             _this.fireEvent('mapClick', this, event);
23774             
23775         });
23776
23777         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23778             _this.fireEvent('mapRightClick', this, event);
23779             
23780         });
23781         
23782         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23783             _this.fireEvent('markerClick', this, event);
23784             
23785         });
23786
23787         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23788             _this.fireEvent('markerRightClick', this, event);
23789             
23790         });
23791         
23792         this.setPosition(this.gMapContext.location);
23793         
23794         this.fireEvent('initial', this, this.gMapContext.location);
23795     },
23796     
23797     initOverlayView: function()
23798     {
23799         var _this = this;
23800         
23801         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23802             
23803             draw: function()
23804             {
23805                 _this.fireEvent('OverlayViewDraw', _this);
23806             },
23807             
23808             onAdd: function()
23809             {
23810                 _this.fireEvent('OverlayViewOnAdd', _this);
23811             },
23812             
23813             onRemove: function()
23814             {
23815                 _this.fireEvent('OverlayViewOnRemove', _this);
23816             },
23817             
23818             show: function(cpx)
23819             {
23820                 _this.fireEvent('OverlayViewShow', _this, cpx);
23821             },
23822             
23823             hide: function()
23824             {
23825                 _this.fireEvent('OverlayViewHide', _this);
23826             }
23827             
23828         });
23829     },
23830     
23831     fromLatLngToContainerPixel: function(event)
23832     {
23833         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23834     },
23835     
23836     isApplied: function() 
23837     {
23838         return this.getGmapContext() == false ? false : true;
23839     },
23840     
23841     getGmapContext: function() 
23842     {
23843         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
23844     },
23845     
23846     GMapContext: function() 
23847     {
23848         var position = new google.maps.LatLng(this.latitude, this.longitude);
23849         
23850         var _map = new google.maps.Map(this.el.dom, {
23851             center: position,
23852             zoom: this.zoom,
23853             mapTypeId: this.mapTypeId,
23854             mapTypeControl: this.mapTypeControl,
23855             disableDoubleClickZoom: this.disableDoubleClickZoom,
23856             scrollwheel: this.scrollwheel,
23857             streetViewControl: this.streetViewControl,
23858             locationName: this.locationName,
23859             draggable: this.draggable,
23860             enableAutocomplete: this.enableAutocomplete,
23861             enableReverseGeocode: this.enableReverseGeocode
23862         });
23863         
23864         var _marker = new google.maps.Marker({
23865             position: position,
23866             map: _map,
23867             title: this.markerTitle,
23868             draggable: this.draggable
23869         });
23870         
23871         return {
23872             map: _map,
23873             marker: _marker,
23874             circle: null,
23875             location: position,
23876             radius: this.radius,
23877             locationName: this.locationName,
23878             addressComponents: {
23879                 formatted_address: null,
23880                 addressLine1: null,
23881                 addressLine2: null,
23882                 streetName: null,
23883                 streetNumber: null,
23884                 city: null,
23885                 district: null,
23886                 state: null,
23887                 stateOrProvince: null
23888             },
23889             settings: this,
23890             domContainer: this.el.dom,
23891             geodecoder: new google.maps.Geocoder()
23892         };
23893     },
23894     
23895     drawCircle: function(center, radius, options) 
23896     {
23897         if (this.gMapContext.circle != null) {
23898             this.gMapContext.circle.setMap(null);
23899         }
23900         if (radius > 0) {
23901             radius *= 1;
23902             options = Roo.apply({}, options, {
23903                 strokeColor: "#0000FF",
23904                 strokeOpacity: .35,
23905                 strokeWeight: 2,
23906                 fillColor: "#0000FF",
23907                 fillOpacity: .2
23908             });
23909             
23910             options.map = this.gMapContext.map;
23911             options.radius = radius;
23912             options.center = center;
23913             this.gMapContext.circle = new google.maps.Circle(options);
23914             return this.gMapContext.circle;
23915         }
23916         
23917         return null;
23918     },
23919     
23920     setPosition: function(location) 
23921     {
23922         this.gMapContext.location = location;
23923         this.gMapContext.marker.setPosition(location);
23924         this.gMapContext.map.panTo(location);
23925         this.drawCircle(location, this.gMapContext.radius, {});
23926         
23927         var _this = this;
23928         
23929         if (this.gMapContext.settings.enableReverseGeocode) {
23930             this.gMapContext.geodecoder.geocode({
23931                 latLng: this.gMapContext.location
23932             }, function(results, status) {
23933                 
23934                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23935                     _this.gMapContext.locationName = results[0].formatted_address;
23936                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23937                     
23938                     _this.fireEvent('positionchanged', this, location);
23939                 }
23940             });
23941             
23942             return;
23943         }
23944         
23945         this.fireEvent('positionchanged', this, location);
23946     },
23947     
23948     resize: function()
23949     {
23950         google.maps.event.trigger(this.gMapContext.map, "resize");
23951         
23952         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23953         
23954         this.fireEvent('resize', this);
23955     },
23956     
23957     setPositionByLatLng: function(latitude, longitude)
23958     {
23959         this.setPosition(new google.maps.LatLng(latitude, longitude));
23960     },
23961     
23962     getCurrentPosition: function() 
23963     {
23964         return {
23965             latitude: this.gMapContext.location.lat(),
23966             longitude: this.gMapContext.location.lng()
23967         };
23968     },
23969     
23970     getAddressName: function() 
23971     {
23972         return this.gMapContext.locationName;
23973     },
23974     
23975     getAddressComponents: function() 
23976     {
23977         return this.gMapContext.addressComponents;
23978     },
23979     
23980     address_component_from_google_geocode: function(address_components) 
23981     {
23982         var result = {};
23983         
23984         for (var i = 0; i < address_components.length; i++) {
23985             var component = address_components[i];
23986             if (component.types.indexOf("postal_code") >= 0) {
23987                 result.postalCode = component.short_name;
23988             } else if (component.types.indexOf("street_number") >= 0) {
23989                 result.streetNumber = component.short_name;
23990             } else if (component.types.indexOf("route") >= 0) {
23991                 result.streetName = component.short_name;
23992             } else if (component.types.indexOf("neighborhood") >= 0) {
23993                 result.city = component.short_name;
23994             } else if (component.types.indexOf("locality") >= 0) {
23995                 result.city = component.short_name;
23996             } else if (component.types.indexOf("sublocality") >= 0) {
23997                 result.district = component.short_name;
23998             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23999                 result.stateOrProvince = component.short_name;
24000             } else if (component.types.indexOf("country") >= 0) {
24001                 result.country = component.short_name;
24002             }
24003         }
24004         
24005         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24006         result.addressLine2 = "";
24007         return result;
24008     },
24009     
24010     setZoomLevel: function(zoom)
24011     {
24012         this.gMapContext.map.setZoom(zoom);
24013     },
24014     
24015     show: function()
24016     {
24017         if(!this.el){
24018             return;
24019         }
24020         
24021         this.el.show();
24022         
24023         this.resize();
24024         
24025         this.fireEvent('show', this);
24026     },
24027     
24028     hide: function()
24029     {
24030         if(!this.el){
24031             return;
24032         }
24033         
24034         this.el.hide();
24035         
24036         this.fireEvent('hide', this);
24037     }
24038     
24039 });
24040
24041 Roo.apply(Roo.bootstrap.LocationPicker, {
24042     
24043     OverlayView : function(map, options)
24044     {
24045         options = options || {};
24046         
24047         this.setMap(map);
24048     }
24049     
24050     
24051 });/*
24052  * - LGPL
24053  *
24054  * Alert
24055  * 
24056  */
24057
24058 /**
24059  * @class Roo.bootstrap.Alert
24060  * @extends Roo.bootstrap.Component
24061  * Bootstrap Alert class
24062  * @cfg {String} title The title of alert
24063  * @cfg {String} html The content of alert
24064  * @cfg {String} weight (  success | info | warning | danger )
24065  * @cfg {String} faicon font-awesomeicon
24066  * 
24067  * @constructor
24068  * Create a new alert
24069  * @param {Object} config The config object
24070  */
24071
24072
24073 Roo.bootstrap.Alert = function(config){
24074     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24075     
24076 };
24077
24078 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24079     
24080     title: '',
24081     html: '',
24082     weight: false,
24083     faicon: false,
24084     
24085     getAutoCreate : function()
24086     {
24087         
24088         var cfg = {
24089             tag : 'div',
24090             cls : 'alert',
24091             cn : [
24092                 {
24093                     tag : 'i',
24094                     cls : 'roo-alert-icon'
24095                     
24096                 },
24097                 {
24098                     tag : 'b',
24099                     cls : 'roo-alert-title',
24100                     html : this.title
24101                 },
24102                 {
24103                     tag : 'span',
24104                     cls : 'roo-alert-text',
24105                     html : this.html
24106                 }
24107             ]
24108         };
24109         
24110         if(this.faicon){
24111             cfg.cn[0].cls += ' fa ' + this.faicon;
24112         }
24113         
24114         if(this.weight){
24115             cfg.cls += ' alert-' + this.weight;
24116         }
24117         
24118         return cfg;
24119     },
24120     
24121     initEvents: function() 
24122     {
24123         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24124     },
24125     
24126     setTitle : function(str)
24127     {
24128         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24129     },
24130     
24131     setText : function(str)
24132     {
24133         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24134     },
24135     
24136     setWeight : function(weight)
24137     {
24138         if(this.weight){
24139             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24140         }
24141         
24142         this.weight = weight;
24143         
24144         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24145     },
24146     
24147     setIcon : function(icon)
24148     {
24149         if(this.faicon){
24150             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24151         }
24152         
24153         this.faicon = icon;
24154         
24155         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24156     },
24157     
24158     hide: function() 
24159     {
24160         this.el.hide();   
24161     },
24162     
24163     show: function() 
24164     {  
24165         this.el.show();   
24166     }
24167     
24168 });
24169
24170  
24171 /*
24172 * Licence: LGPL
24173 */
24174
24175 /**
24176  * @class Roo.bootstrap.UploadCropbox
24177  * @extends Roo.bootstrap.Component
24178  * Bootstrap UploadCropbox class
24179  * @cfg {String} emptyText show when image has been loaded
24180  * @cfg {String} rotateNotify show when image too small to rotate
24181  * @cfg {Number} errorTimeout default 3000
24182  * @cfg {Number} minWidth default 300
24183  * @cfg {Number} minHeight default 300
24184  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24185  * @cfg {Boolean} isDocument (true|false) default false
24186  * @cfg {String} url action url
24187  * @cfg {String} paramName default 'imageUpload'
24188  * @cfg {String} method default POST
24189  * 
24190  * @constructor
24191  * Create a new UploadCropbox
24192  * @param {Object} config The config object
24193  */
24194
24195 Roo.bootstrap.UploadCropbox = function(config){
24196     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24197     
24198     this.addEvents({
24199         /**
24200          * @event beforeselectfile
24201          * Fire before select file
24202          * @param {Roo.bootstrap.UploadCropbox} this
24203          */
24204         "beforeselectfile" : true,
24205         /**
24206          * @event initial
24207          * Fire after initEvent
24208          * @param {Roo.bootstrap.UploadCropbox} this
24209          */
24210         "initial" : true,
24211         /**
24212          * @event crop
24213          * Fire after initEvent
24214          * @param {Roo.bootstrap.UploadCropbox} this
24215          * @param {String} data
24216          */
24217         "crop" : true,
24218         /**
24219          * @event prepare
24220          * Fire when preparing the file data
24221          * @param {Roo.bootstrap.UploadCropbox} this
24222          * @param {Object} file
24223          */
24224         "prepare" : true,
24225         /**
24226          * @event exception
24227          * Fire when get exception
24228          * @param {Roo.bootstrap.UploadCropbox} this
24229          * @param {XMLHttpRequest} xhr
24230          */
24231         "exception" : true,
24232         /**
24233          * @event beforeloadcanvas
24234          * Fire before load the canvas
24235          * @param {Roo.bootstrap.UploadCropbox} this
24236          * @param {String} src
24237          */
24238         "beforeloadcanvas" : true,
24239         /**
24240          * @event trash
24241          * Fire when trash image
24242          * @param {Roo.bootstrap.UploadCropbox} this
24243          */
24244         "trash" : true,
24245         /**
24246          * @event download
24247          * Fire when download the image
24248          * @param {Roo.bootstrap.UploadCropbox} this
24249          */
24250         "download" : true,
24251         /**
24252          * @event footerbuttonclick
24253          * Fire when footerbuttonclick
24254          * @param {Roo.bootstrap.UploadCropbox} this
24255          * @param {String} type
24256          */
24257         "footerbuttonclick" : true,
24258         /**
24259          * @event resize
24260          * Fire when resize
24261          * @param {Roo.bootstrap.UploadCropbox} this
24262          */
24263         "resize" : true,
24264         /**
24265          * @event rotate
24266          * Fire when rotate the image
24267          * @param {Roo.bootstrap.UploadCropbox} this
24268          * @param {String} pos
24269          */
24270         "rotate" : true,
24271         /**
24272          * @event inspect
24273          * Fire when inspect the file
24274          * @param {Roo.bootstrap.UploadCropbox} this
24275          * @param {Object} file
24276          */
24277         "inspect" : true,
24278         /**
24279          * @event upload
24280          * Fire when xhr upload the file
24281          * @param {Roo.bootstrap.UploadCropbox} this
24282          * @param {Object} data
24283          */
24284         "upload" : true,
24285         /**
24286          * @event arrange
24287          * Fire when arrange the file data
24288          * @param {Roo.bootstrap.UploadCropbox} this
24289          * @param {Object} formData
24290          */
24291         "arrange" : true
24292     });
24293     
24294     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24295 };
24296
24297 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24298     
24299     emptyText : 'Click to upload image',
24300     rotateNotify : 'Image is too small to rotate',
24301     errorTimeout : 3000,
24302     scale : 0,
24303     baseScale : 1,
24304     rotate : 0,
24305     dragable : false,
24306     pinching : false,
24307     mouseX : 0,
24308     mouseY : 0,
24309     cropData : false,
24310     minWidth : 300,
24311     minHeight : 300,
24312     file : false,
24313     exif : {},
24314     baseRotate : 1,
24315     cropType : 'image/jpeg',
24316     buttons : false,
24317     canvasLoaded : false,
24318     isDocument : false,
24319     method : 'POST',
24320     paramName : 'imageUpload',
24321     
24322     getAutoCreate : function()
24323     {
24324         var cfg = {
24325             tag : 'div',
24326             cls : 'roo-upload-cropbox',
24327             cn : [
24328                 {
24329                     tag : 'input',
24330                     cls : 'roo-upload-cropbox-selector',
24331                     type : 'file'
24332                 },
24333                 {
24334                     tag : 'div',
24335                     cls : 'roo-upload-cropbox-body',
24336                     style : 'cursor:pointer',
24337                     cn : [
24338                         {
24339                             tag : 'div',
24340                             cls : 'roo-upload-cropbox-preview'
24341                         },
24342                         {
24343                             tag : 'div',
24344                             cls : 'roo-upload-cropbox-thumb'
24345                         },
24346                         {
24347                             tag : 'div',
24348                             cls : 'roo-upload-cropbox-empty-notify',
24349                             html : this.emptyText
24350                         },
24351                         {
24352                             tag : 'div',
24353                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24354                             html : this.rotateNotify
24355                         }
24356                     ]
24357                 },
24358                 {
24359                     tag : 'div',
24360                     cls : 'roo-upload-cropbox-footer',
24361                     cn : {
24362                         tag : 'div',
24363                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24364                         cn : []
24365                     }
24366                 }
24367             ]
24368         };
24369         
24370         return cfg;
24371     },
24372     
24373     onRender : function(ct, position)
24374     {
24375         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24376         
24377         if (this.buttons.length) {
24378             
24379             Roo.each(this.buttons, function(bb) {
24380                 
24381                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24382                 
24383                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24384                 
24385             }, this);
24386         }
24387     },
24388     
24389     initEvents : function()
24390     {
24391         this.urlAPI = (window.createObjectURL && window) || 
24392                                 (window.URL && URL.revokeObjectURL && URL) || 
24393                                 (window.webkitURL && webkitURL);
24394                         
24395         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24396         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24397         
24398         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24399         this.selectorEl.hide();
24400         
24401         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24402         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24403         
24404         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24405         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24406         this.thumbEl.hide();
24407         
24408         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24409         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24410         
24411         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24412         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24413         this.errorEl.hide();
24414         
24415         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24416         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24417         this.footerEl.hide();
24418         
24419         this.setThumbBoxSize();
24420         
24421         this.bind();
24422         
24423         this.resize();
24424         
24425         this.fireEvent('initial', this);
24426     },
24427
24428     bind : function()
24429     {
24430         var _this = this;
24431         
24432         window.addEventListener("resize", function() { _this.resize(); } );
24433         
24434         this.bodyEl.on('click', this.beforeSelectFile, this);
24435         
24436         if(Roo.isTouch){
24437             this.bodyEl.on('touchstart', this.onTouchStart, this);
24438             this.bodyEl.on('touchmove', this.onTouchMove, this);
24439             this.bodyEl.on('touchend', this.onTouchEnd, this);
24440         }
24441         
24442         if(!Roo.isTouch){
24443             this.bodyEl.on('mousedown', this.onMouseDown, this);
24444             this.bodyEl.on('mousemove', this.onMouseMove, this);
24445             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24446             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24447             Roo.get(document).on('mouseup', this.onMouseUp, this);
24448         }
24449         
24450         this.selectorEl.on('change', this.onFileSelected, this);
24451     },
24452     
24453     reset : function()
24454     {    
24455         this.scale = 0;
24456         this.baseScale = 1;
24457         this.rotate = 0;
24458         this.baseRotate = 1;
24459         this.dragable = false;
24460         this.pinching = false;
24461         this.mouseX = 0;
24462         this.mouseY = 0;
24463         this.cropData = false;
24464         this.notifyEl.dom.innerHTML = this.emptyText;
24465         
24466         this.selectorEl.dom.value = '';
24467         
24468     },
24469     
24470     resize : function()
24471     {
24472         if(this.fireEvent('resize', this) != false){
24473             this.setThumbBoxPosition();
24474             this.setCanvasPosition();
24475         }
24476     },
24477     
24478     onFooterButtonClick : function(e, el, o, type)
24479     {
24480         switch (type) {
24481             case 'rotate-left' :
24482                 this.onRotateLeft(e);
24483                 break;
24484             case 'rotate-right' :
24485                 this.onRotateRight(e);
24486                 break;
24487             case 'picture' :
24488                 this.beforeSelectFile(e);
24489                 break;
24490             case 'trash' :
24491                 this.trash(e);
24492                 break;
24493             case 'crop' :
24494                 this.crop(e);
24495                 break;
24496             case 'download' :
24497                 this.download(e);
24498                 break;
24499             default :
24500                 break;
24501         }
24502         
24503         this.fireEvent('footerbuttonclick', this, type);
24504     },
24505     
24506     beforeSelectFile : function(e)
24507     {
24508         e.preventDefault();
24509         
24510         if(this.fireEvent('beforeselectfile', this) != false){
24511             this.selectorEl.dom.click();
24512         }
24513     },
24514     
24515     onFileSelected : function(e)
24516     {
24517         e.preventDefault();
24518         
24519         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24520             return;
24521         }
24522         
24523         var file = this.selectorEl.dom.files[0];
24524         
24525         if(this.fireEvent('inspect', this, file) != false){
24526             this.prepare(file);
24527         }
24528         
24529     },
24530     
24531     trash : function(e)
24532     {
24533         this.fireEvent('trash', this);
24534     },
24535     
24536     download : function(e)
24537     {
24538         this.fireEvent('download', this);
24539     },
24540     
24541     loadCanvas : function(src)
24542     {   
24543         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24544             
24545             this.reset();
24546             
24547             this.imageEl = document.createElement('img');
24548             
24549             var _this = this;
24550             
24551             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24552             
24553             this.imageEl.src = src;
24554         }
24555     },
24556     
24557     onLoadCanvas : function()
24558     {   
24559         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24560         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24561         
24562         this.bodyEl.un('click', this.beforeSelectFile, this);
24563         
24564         this.notifyEl.hide();
24565         this.thumbEl.show();
24566         this.footerEl.show();
24567         
24568         this.baseRotateLevel();
24569         
24570         if(this.isDocument){
24571             this.setThumbBoxSize();
24572         }
24573         
24574         this.setThumbBoxPosition();
24575         
24576         this.baseScaleLevel();
24577         
24578         this.draw();
24579         
24580         this.resize();
24581         
24582         this.canvasLoaded = true;
24583         
24584     },
24585     
24586     setCanvasPosition : function()
24587     {   
24588         if(!this.canvasEl){
24589             return;
24590         }
24591         
24592         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24593         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24594         
24595         this.previewEl.setLeft(pw);
24596         this.previewEl.setTop(ph);
24597         
24598     },
24599     
24600     onMouseDown : function(e)
24601     {   
24602         e.stopEvent();
24603         
24604         this.dragable = true;
24605         this.pinching = false;
24606         
24607         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24608             this.dragable = false;
24609             return;
24610         }
24611         
24612         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24613         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24614         
24615     },
24616     
24617     onMouseMove : function(e)
24618     {   
24619         e.stopEvent();
24620         
24621         if(!this.canvasLoaded){
24622             return;
24623         }
24624         
24625         if (!this.dragable){
24626             return;
24627         }
24628         
24629         var minX = Math.ceil(this.thumbEl.getLeft(true));
24630         var minY = Math.ceil(this.thumbEl.getTop(true));
24631         
24632         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24633         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24634         
24635         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24636         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24637         
24638         x = x - this.mouseX;
24639         y = y - this.mouseY;
24640         
24641         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24642         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24643         
24644         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24645         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24646         
24647         this.previewEl.setLeft(bgX);
24648         this.previewEl.setTop(bgY);
24649         
24650         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24651         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24652     },
24653     
24654     onMouseUp : function(e)
24655     {   
24656         e.stopEvent();
24657         
24658         this.dragable = false;
24659     },
24660     
24661     onMouseWheel : function(e)
24662     {   
24663         e.stopEvent();
24664         
24665         this.startScale = this.scale;
24666         
24667         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24668         
24669         if(!this.zoomable()){
24670             this.scale = this.startScale;
24671             return;
24672         }
24673         
24674         this.draw();
24675         
24676         return;
24677     },
24678     
24679     zoomable : function()
24680     {
24681         var minScale = this.thumbEl.getWidth() / this.minWidth;
24682         
24683         if(this.minWidth < this.minHeight){
24684             minScale = this.thumbEl.getHeight() / this.minHeight;
24685         }
24686         
24687         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24688         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24689         
24690         if(
24691                 this.isDocument &&
24692                 (this.rotate == 0 || this.rotate == 180) && 
24693                 (
24694                     width > this.imageEl.OriginWidth || 
24695                     height > this.imageEl.OriginHeight ||
24696                     (width < this.minWidth && height < this.minHeight)
24697                 )
24698         ){
24699             return false;
24700         }
24701         
24702         if(
24703                 this.isDocument &&
24704                 (this.rotate == 90 || this.rotate == 270) && 
24705                 (
24706                     width > this.imageEl.OriginWidth || 
24707                     height > this.imageEl.OriginHeight ||
24708                     (width < this.minHeight && height < this.minWidth)
24709                 )
24710         ){
24711             return false;
24712         }
24713         
24714         if(
24715                 !this.isDocument &&
24716                 (this.rotate == 0 || this.rotate == 180) && 
24717                 (
24718                     width < this.minWidth || 
24719                     width > this.imageEl.OriginWidth || 
24720                     height < this.minHeight || 
24721                     height > this.imageEl.OriginHeight
24722                 )
24723         ){
24724             return false;
24725         }
24726         
24727         if(
24728                 !this.isDocument &&
24729                 (this.rotate == 90 || this.rotate == 270) && 
24730                 (
24731                     width < this.minHeight || 
24732                     width > this.imageEl.OriginWidth || 
24733                     height < this.minWidth || 
24734                     height > this.imageEl.OriginHeight
24735                 )
24736         ){
24737             return false;
24738         }
24739         
24740         return true;
24741         
24742     },
24743     
24744     onRotateLeft : function(e)
24745     {   
24746         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24747             
24748             var minScale = this.thumbEl.getWidth() / this.minWidth;
24749             
24750             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24751             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24752             
24753             this.startScale = this.scale;
24754             
24755             while (this.getScaleLevel() < minScale){
24756             
24757                 this.scale = this.scale + 1;
24758                 
24759                 if(!this.zoomable()){
24760                     break;
24761                 }
24762                 
24763                 if(
24764                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24765                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24766                 ){
24767                     continue;
24768                 }
24769                 
24770                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24771
24772                 this.draw();
24773                 
24774                 return;
24775             }
24776             
24777             this.scale = this.startScale;
24778             
24779             this.onRotateFail();
24780             
24781             return false;
24782         }
24783         
24784         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24785
24786         if(this.isDocument){
24787             this.setThumbBoxSize();
24788             this.setThumbBoxPosition();
24789             this.setCanvasPosition();
24790         }
24791         
24792         this.draw();
24793         
24794         this.fireEvent('rotate', this, 'left');
24795         
24796     },
24797     
24798     onRotateRight : function(e)
24799     {
24800         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24801             
24802             var minScale = this.thumbEl.getWidth() / this.minWidth;
24803         
24804             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24805             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24806             
24807             this.startScale = this.scale;
24808             
24809             while (this.getScaleLevel() < minScale){
24810             
24811                 this.scale = this.scale + 1;
24812                 
24813                 if(!this.zoomable()){
24814                     break;
24815                 }
24816                 
24817                 if(
24818                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24819                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24820                 ){
24821                     continue;
24822                 }
24823                 
24824                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24825
24826                 this.draw();
24827                 
24828                 return;
24829             }
24830             
24831             this.scale = this.startScale;
24832             
24833             this.onRotateFail();
24834             
24835             return false;
24836         }
24837         
24838         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24839
24840         if(this.isDocument){
24841             this.setThumbBoxSize();
24842             this.setThumbBoxPosition();
24843             this.setCanvasPosition();
24844         }
24845         
24846         this.draw();
24847         
24848         this.fireEvent('rotate', this, 'right');
24849     },
24850     
24851     onRotateFail : function()
24852     {
24853         this.errorEl.show(true);
24854         
24855         var _this = this;
24856         
24857         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24858     },
24859     
24860     draw : function()
24861     {
24862         this.previewEl.dom.innerHTML = '';
24863         
24864         var canvasEl = document.createElement("canvas");
24865         
24866         var contextEl = canvasEl.getContext("2d");
24867         
24868         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24869         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24870         var center = this.imageEl.OriginWidth / 2;
24871         
24872         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24873             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24874             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24875             center = this.imageEl.OriginHeight / 2;
24876         }
24877         
24878         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24879         
24880         contextEl.translate(center, center);
24881         contextEl.rotate(this.rotate * Math.PI / 180);
24882
24883         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24884         
24885         this.canvasEl = document.createElement("canvas");
24886         
24887         this.contextEl = this.canvasEl.getContext("2d");
24888         
24889         switch (this.rotate) {
24890             case 0 :
24891                 
24892                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24893                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24894                 
24895                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24896                 
24897                 break;
24898             case 90 : 
24899                 
24900                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24901                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24902                 
24903                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24904                     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);
24905                     break;
24906                 }
24907                 
24908                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24909                 
24910                 break;
24911             case 180 :
24912                 
24913                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24914                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24915                 
24916                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24917                     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);
24918                     break;
24919                 }
24920                 
24921                 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);
24922                 
24923                 break;
24924             case 270 :
24925                 
24926                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24927                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24928         
24929                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24930                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24931                     break;
24932                 }
24933                 
24934                 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);
24935                 
24936                 break;
24937             default : 
24938                 break;
24939         }
24940         
24941         this.previewEl.appendChild(this.canvasEl);
24942         
24943         this.setCanvasPosition();
24944     },
24945     
24946     crop : function()
24947     {
24948         if(!this.canvasLoaded){
24949             return;
24950         }
24951         
24952         var imageCanvas = document.createElement("canvas");
24953         
24954         var imageContext = imageCanvas.getContext("2d");
24955         
24956         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24957         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24958         
24959         var center = imageCanvas.width / 2;
24960         
24961         imageContext.translate(center, center);
24962         
24963         imageContext.rotate(this.rotate * Math.PI / 180);
24964         
24965         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24966         
24967         var canvas = document.createElement("canvas");
24968         
24969         var context = canvas.getContext("2d");
24970                 
24971         canvas.width = this.minWidth;
24972         canvas.height = this.minHeight;
24973
24974         switch (this.rotate) {
24975             case 0 :
24976                 
24977                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
24978                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
24979                 
24980                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24981                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24982                 
24983                 var targetWidth = this.minWidth - 2 * x;
24984                 var targetHeight = this.minHeight - 2 * y;
24985                 
24986                 var scale = 1;
24987                 
24988                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24989                     scale = targetWidth / width;
24990                 }
24991                 
24992                 if(x > 0 && y == 0){
24993                     scale = targetHeight / height;
24994                 }
24995                 
24996                 if(x > 0 && y > 0){
24997                     scale = targetWidth / width;
24998                     
24999                     if(width < height){
25000                         scale = targetHeight / height;
25001                     }
25002                 }
25003                 
25004                 context.scale(scale, scale);
25005                 
25006                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25007                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25008
25009                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25010                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25011
25012                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25013                 
25014                 break;
25015             case 90 : 
25016                 
25017                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25018                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25019                 
25020                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25021                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25022                 
25023                 var targetWidth = this.minWidth - 2 * x;
25024                 var targetHeight = this.minHeight - 2 * y;
25025                 
25026                 var scale = 1;
25027                 
25028                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25029                     scale = targetWidth / width;
25030                 }
25031                 
25032                 if(x > 0 && y == 0){
25033                     scale = targetHeight / height;
25034                 }
25035                 
25036                 if(x > 0 && y > 0){
25037                     scale = targetWidth / width;
25038                     
25039                     if(width < height){
25040                         scale = targetHeight / height;
25041                     }
25042                 }
25043                 
25044                 context.scale(scale, scale);
25045                 
25046                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25047                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25048
25049                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25050                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25051                 
25052                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25053                 
25054                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25055                 
25056                 break;
25057             case 180 :
25058                 
25059                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25060                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25061                 
25062                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25063                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25064                 
25065                 var targetWidth = this.minWidth - 2 * x;
25066                 var targetHeight = this.minHeight - 2 * y;
25067                 
25068                 var scale = 1;
25069                 
25070                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25071                     scale = targetWidth / width;
25072                 }
25073                 
25074                 if(x > 0 && y == 0){
25075                     scale = targetHeight / height;
25076                 }
25077                 
25078                 if(x > 0 && y > 0){
25079                     scale = targetWidth / width;
25080                     
25081                     if(width < height){
25082                         scale = targetHeight / height;
25083                     }
25084                 }
25085                 
25086                 context.scale(scale, scale);
25087                 
25088                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25089                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25090
25091                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25092                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25093
25094                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25095                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25096                 
25097                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25098                 
25099                 break;
25100             case 270 :
25101                 
25102                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25103                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25104                 
25105                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25106                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25107                 
25108                 var targetWidth = this.minWidth - 2 * x;
25109                 var targetHeight = this.minHeight - 2 * y;
25110                 
25111                 var scale = 1;
25112                 
25113                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25114                     scale = targetWidth / width;
25115                 }
25116                 
25117                 if(x > 0 && y == 0){
25118                     scale = targetHeight / height;
25119                 }
25120                 
25121                 if(x > 0 && y > 0){
25122                     scale = targetWidth / width;
25123                     
25124                     if(width < height){
25125                         scale = targetHeight / height;
25126                     }
25127                 }
25128                 
25129                 context.scale(scale, scale);
25130                 
25131                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25132                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25133
25134                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25135                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25136                 
25137                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25138                 
25139                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25140                 
25141                 break;
25142             default : 
25143                 break;
25144         }
25145         
25146         this.cropData = canvas.toDataURL(this.cropType);
25147         
25148         if(this.fireEvent('crop', this, this.cropData) !== false){
25149             this.process(this.file, this.cropData);
25150         }
25151         
25152         return;
25153         
25154     },
25155     
25156     setThumbBoxSize : function()
25157     {
25158         var width, height;
25159         
25160         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25161             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25162             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25163             
25164             this.minWidth = width;
25165             this.minHeight = height;
25166             
25167             if(this.rotate == 90 || this.rotate == 270){
25168                 this.minWidth = height;
25169                 this.minHeight = width;
25170             }
25171         }
25172         
25173         height = 300;
25174         width = Math.ceil(this.minWidth * height / this.minHeight);
25175         
25176         if(this.minWidth > this.minHeight){
25177             width = 300;
25178             height = Math.ceil(this.minHeight * width / this.minWidth);
25179         }
25180         
25181         this.thumbEl.setStyle({
25182             width : width + 'px',
25183             height : height + 'px'
25184         });
25185
25186         return;
25187             
25188     },
25189     
25190     setThumbBoxPosition : function()
25191     {
25192         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25193         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25194         
25195         this.thumbEl.setLeft(x);
25196         this.thumbEl.setTop(y);
25197         
25198     },
25199     
25200     baseRotateLevel : function()
25201     {
25202         this.baseRotate = 1;
25203         
25204         if(
25205                 typeof(this.exif) != 'undefined' &&
25206                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25207                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25208         ){
25209             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25210         }
25211         
25212         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25213         
25214     },
25215     
25216     baseScaleLevel : function()
25217     {
25218         var width, height;
25219         
25220         if(this.isDocument){
25221             
25222             if(this.baseRotate == 6 || this.baseRotate == 8){
25223             
25224                 height = this.thumbEl.getHeight();
25225                 this.baseScale = height / this.imageEl.OriginWidth;
25226
25227                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25228                     width = this.thumbEl.getWidth();
25229                     this.baseScale = width / this.imageEl.OriginHeight;
25230                 }
25231
25232                 return;
25233             }
25234
25235             height = this.thumbEl.getHeight();
25236             this.baseScale = height / this.imageEl.OriginHeight;
25237
25238             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25239                 width = this.thumbEl.getWidth();
25240                 this.baseScale = width / this.imageEl.OriginWidth;
25241             }
25242
25243             return;
25244         }
25245         
25246         if(this.baseRotate == 6 || this.baseRotate == 8){
25247             
25248             width = this.thumbEl.getHeight();
25249             this.baseScale = width / this.imageEl.OriginHeight;
25250             
25251             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25252                 height = this.thumbEl.getWidth();
25253                 this.baseScale = height / this.imageEl.OriginHeight;
25254             }
25255             
25256             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25257                 height = this.thumbEl.getWidth();
25258                 this.baseScale = height / this.imageEl.OriginHeight;
25259                 
25260                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25261                     width = this.thumbEl.getHeight();
25262                     this.baseScale = width / this.imageEl.OriginWidth;
25263                 }
25264             }
25265             
25266             return;
25267         }
25268         
25269         width = this.thumbEl.getWidth();
25270         this.baseScale = width / this.imageEl.OriginWidth;
25271         
25272         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25273             height = this.thumbEl.getHeight();
25274             this.baseScale = height / this.imageEl.OriginHeight;
25275         }
25276         
25277         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25278             
25279             height = this.thumbEl.getHeight();
25280             this.baseScale = height / this.imageEl.OriginHeight;
25281             
25282             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25283                 width = this.thumbEl.getWidth();
25284                 this.baseScale = width / this.imageEl.OriginWidth;
25285             }
25286             
25287         }
25288         
25289         return;
25290     },
25291     
25292     getScaleLevel : function()
25293     {
25294         return this.baseScale * Math.pow(1.1, this.scale);
25295     },
25296     
25297     onTouchStart : function(e)
25298     {
25299         if(!this.canvasLoaded){
25300             this.beforeSelectFile(e);
25301             return;
25302         }
25303         
25304         var touches = e.browserEvent.touches;
25305         
25306         if(!touches){
25307             return;
25308         }
25309         
25310         if(touches.length == 1){
25311             this.onMouseDown(e);
25312             return;
25313         }
25314         
25315         if(touches.length != 2){
25316             return;
25317         }
25318         
25319         var coords = [];
25320         
25321         for(var i = 0, finger; finger = touches[i]; i++){
25322             coords.push(finger.pageX, finger.pageY);
25323         }
25324         
25325         var x = Math.pow(coords[0] - coords[2], 2);
25326         var y = Math.pow(coords[1] - coords[3], 2);
25327         
25328         this.startDistance = Math.sqrt(x + y);
25329         
25330         this.startScale = this.scale;
25331         
25332         this.pinching = true;
25333         this.dragable = false;
25334         
25335     },
25336     
25337     onTouchMove : function(e)
25338     {
25339         if(!this.pinching && !this.dragable){
25340             return;
25341         }
25342         
25343         var touches = e.browserEvent.touches;
25344         
25345         if(!touches){
25346             return;
25347         }
25348         
25349         if(this.dragable){
25350             this.onMouseMove(e);
25351             return;
25352         }
25353         
25354         var coords = [];
25355         
25356         for(var i = 0, finger; finger = touches[i]; i++){
25357             coords.push(finger.pageX, finger.pageY);
25358         }
25359         
25360         var x = Math.pow(coords[0] - coords[2], 2);
25361         var y = Math.pow(coords[1] - coords[3], 2);
25362         
25363         this.endDistance = Math.sqrt(x + y);
25364         
25365         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25366         
25367         if(!this.zoomable()){
25368             this.scale = this.startScale;
25369             return;
25370         }
25371         
25372         this.draw();
25373         
25374     },
25375     
25376     onTouchEnd : function(e)
25377     {
25378         this.pinching = false;
25379         this.dragable = false;
25380         
25381     },
25382     
25383     process : function(file, crop)
25384     {
25385         this.xhr = new XMLHttpRequest();
25386         
25387         file.xhr = this.xhr;
25388
25389         this.xhr.open(this.method, this.url, true);
25390         
25391         var headers = {
25392             "Accept": "application/json",
25393             "Cache-Control": "no-cache",
25394             "X-Requested-With": "XMLHttpRequest"
25395         };
25396         
25397         for (var headerName in headers) {
25398             var headerValue = headers[headerName];
25399             if (headerValue) {
25400                 this.xhr.setRequestHeader(headerName, headerValue);
25401             }
25402         }
25403         
25404         var _this = this;
25405         
25406         this.xhr.onload = function()
25407         {
25408             _this.xhrOnLoad(_this.xhr);
25409         }
25410         
25411         this.xhr.onerror = function()
25412         {
25413             _this.xhrOnError(_this.xhr);
25414         }
25415         
25416         var formData = new FormData();
25417
25418         formData.append('returnHTML', 'NO');
25419         
25420         if(crop){
25421             formData.append('crop', crop);
25422         }
25423         
25424         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25425             formData.append(this.paramName, file, file.name);
25426         }
25427         
25428         if(typeof(file.filename) != 'undefined'){
25429             formData.append('filename', file.filename);
25430         }
25431         
25432         if(typeof(file.mimetype) != 'undefined'){
25433             formData.append('mimetype', file.mimetype);
25434         }
25435         
25436         if(this.fireEvent('arrange', this, formData) != false){
25437             this.xhr.send(formData);
25438         };
25439     },
25440     
25441     xhrOnLoad : function(xhr)
25442     {
25443         if (xhr.readyState !== 4) {
25444             this.fireEvent('exception', this, xhr);
25445             return;
25446         }
25447
25448         var response = Roo.decode(xhr.responseText);
25449         
25450         if(!response.success){
25451             this.fireEvent('exception', this, xhr);
25452             return;
25453         }
25454         
25455         var response = Roo.decode(xhr.responseText);
25456         
25457         this.fireEvent('upload', this, response);
25458         
25459     },
25460     
25461     xhrOnError : function()
25462     {
25463         Roo.log('xhr on error');
25464         
25465         var response = Roo.decode(xhr.responseText);
25466           
25467         Roo.log(response);
25468         
25469     },
25470     
25471     prepare : function(file)
25472     {   
25473         this.file = false;
25474         this.exif = {};
25475         
25476         if(typeof(file) === 'string'){
25477             this.loadCanvas(file);
25478             return;
25479         }
25480         
25481         if(!file || !this.urlAPI){
25482             return;
25483         }
25484         
25485         this.file = file;
25486         this.cropType = file.type;
25487         
25488         var _this = this;
25489         
25490         if(this.fireEvent('prepare', this, this.file) != false){
25491             
25492             var reader = new FileReader();
25493             
25494             reader.onload = function (e) {
25495                 if (e.target.error) {
25496                     Roo.log(e.target.error);
25497                     return;
25498                 }
25499                 
25500                 var buffer = e.target.result,
25501                     dataView = new DataView(buffer),
25502                     offset = 2,
25503                     maxOffset = dataView.byteLength - 4,
25504                     markerBytes,
25505                     markerLength;
25506                 
25507                 if (dataView.getUint16(0) === 0xffd8) {
25508                     while (offset < maxOffset) {
25509                         markerBytes = dataView.getUint16(offset);
25510                         
25511                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25512                             markerLength = dataView.getUint16(offset + 2) + 2;
25513                             if (offset + markerLength > dataView.byteLength) {
25514                                 Roo.log('Invalid meta data: Invalid segment size.');
25515                                 break;
25516                             }
25517                             
25518                             if(markerBytes == 0xffe1){
25519                                 _this.parseExifData(
25520                                     dataView,
25521                                     offset,
25522                                     markerLength
25523                                 );
25524                             }
25525                             
25526                             offset += markerLength;
25527                             
25528                             continue;
25529                         }
25530                         
25531                         break;
25532                     }
25533                     
25534                 }
25535                 
25536                 var url = _this.urlAPI.createObjectURL(_this.file);
25537                 
25538                 _this.loadCanvas(url);
25539                 
25540                 return;
25541             }
25542             
25543             reader.readAsArrayBuffer(this.file);
25544             
25545         }
25546         
25547     },
25548     
25549     parseExifData : function(dataView, offset, length)
25550     {
25551         var tiffOffset = offset + 10,
25552             littleEndian,
25553             dirOffset;
25554     
25555         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25556             // No Exif data, might be XMP data instead
25557             return;
25558         }
25559         
25560         // Check for the ASCII code for "Exif" (0x45786966):
25561         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25562             // No Exif data, might be XMP data instead
25563             return;
25564         }
25565         if (tiffOffset + 8 > dataView.byteLength) {
25566             Roo.log('Invalid Exif data: Invalid segment size.');
25567             return;
25568         }
25569         // Check for the two null bytes:
25570         if (dataView.getUint16(offset + 8) !== 0x0000) {
25571             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25572             return;
25573         }
25574         // Check the byte alignment:
25575         switch (dataView.getUint16(tiffOffset)) {
25576         case 0x4949:
25577             littleEndian = true;
25578             break;
25579         case 0x4D4D:
25580             littleEndian = false;
25581             break;
25582         default:
25583             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25584             return;
25585         }
25586         // Check for the TIFF tag marker (0x002A):
25587         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25588             Roo.log('Invalid Exif data: Missing TIFF marker.');
25589             return;
25590         }
25591         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25592         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25593         
25594         this.parseExifTags(
25595             dataView,
25596             tiffOffset,
25597             tiffOffset + dirOffset,
25598             littleEndian
25599         );
25600     },
25601     
25602     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25603     {
25604         var tagsNumber,
25605             dirEndOffset,
25606             i;
25607         if (dirOffset + 6 > dataView.byteLength) {
25608             Roo.log('Invalid Exif data: Invalid directory offset.');
25609             return;
25610         }
25611         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25612         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25613         if (dirEndOffset + 4 > dataView.byteLength) {
25614             Roo.log('Invalid Exif data: Invalid directory size.');
25615             return;
25616         }
25617         for (i = 0; i < tagsNumber; i += 1) {
25618             this.parseExifTag(
25619                 dataView,
25620                 tiffOffset,
25621                 dirOffset + 2 + 12 * i, // tag offset
25622                 littleEndian
25623             );
25624         }
25625         // Return the offset to the next directory:
25626         return dataView.getUint32(dirEndOffset, littleEndian);
25627     },
25628     
25629     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25630     {
25631         var tag = dataView.getUint16(offset, littleEndian);
25632         
25633         this.exif[tag] = this.getExifValue(
25634             dataView,
25635             tiffOffset,
25636             offset,
25637             dataView.getUint16(offset + 2, littleEndian), // tag type
25638             dataView.getUint32(offset + 4, littleEndian), // tag length
25639             littleEndian
25640         );
25641     },
25642     
25643     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25644     {
25645         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25646             tagSize,
25647             dataOffset,
25648             values,
25649             i,
25650             str,
25651             c;
25652     
25653         if (!tagType) {
25654             Roo.log('Invalid Exif data: Invalid tag type.');
25655             return;
25656         }
25657         
25658         tagSize = tagType.size * length;
25659         // Determine if the value is contained in the dataOffset bytes,
25660         // or if the value at the dataOffset is a pointer to the actual data:
25661         dataOffset = tagSize > 4 ?
25662                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25663         if (dataOffset + tagSize > dataView.byteLength) {
25664             Roo.log('Invalid Exif data: Invalid data offset.');
25665             return;
25666         }
25667         if (length === 1) {
25668             return tagType.getValue(dataView, dataOffset, littleEndian);
25669         }
25670         values = [];
25671         for (i = 0; i < length; i += 1) {
25672             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25673         }
25674         
25675         if (tagType.ascii) {
25676             str = '';
25677             // Concatenate the chars:
25678             for (i = 0; i < values.length; i += 1) {
25679                 c = values[i];
25680                 // Ignore the terminating NULL byte(s):
25681                 if (c === '\u0000') {
25682                     break;
25683                 }
25684                 str += c;
25685             }
25686             return str;
25687         }
25688         return values;
25689     }
25690     
25691 });
25692
25693 Roo.apply(Roo.bootstrap.UploadCropbox, {
25694     tags : {
25695         'Orientation': 0x0112
25696     },
25697     
25698     Orientation: {
25699             1: 0, //'top-left',
25700 //            2: 'top-right',
25701             3: 180, //'bottom-right',
25702 //            4: 'bottom-left',
25703 //            5: 'left-top',
25704             6: 90, //'right-top',
25705 //            7: 'right-bottom',
25706             8: 270 //'left-bottom'
25707     },
25708     
25709     exifTagTypes : {
25710         // byte, 8-bit unsigned int:
25711         1: {
25712             getValue: function (dataView, dataOffset) {
25713                 return dataView.getUint8(dataOffset);
25714             },
25715             size: 1
25716         },
25717         // ascii, 8-bit byte:
25718         2: {
25719             getValue: function (dataView, dataOffset) {
25720                 return String.fromCharCode(dataView.getUint8(dataOffset));
25721             },
25722             size: 1,
25723             ascii: true
25724         },
25725         // short, 16 bit int:
25726         3: {
25727             getValue: function (dataView, dataOffset, littleEndian) {
25728                 return dataView.getUint16(dataOffset, littleEndian);
25729             },
25730             size: 2
25731         },
25732         // long, 32 bit int:
25733         4: {
25734             getValue: function (dataView, dataOffset, littleEndian) {
25735                 return dataView.getUint32(dataOffset, littleEndian);
25736             },
25737             size: 4
25738         },
25739         // rational = two long values, first is numerator, second is denominator:
25740         5: {
25741             getValue: function (dataView, dataOffset, littleEndian) {
25742                 return dataView.getUint32(dataOffset, littleEndian) /
25743                     dataView.getUint32(dataOffset + 4, littleEndian);
25744             },
25745             size: 8
25746         },
25747         // slong, 32 bit signed int:
25748         9: {
25749             getValue: function (dataView, dataOffset, littleEndian) {
25750                 return dataView.getInt32(dataOffset, littleEndian);
25751             },
25752             size: 4
25753         },
25754         // srational, two slongs, first is numerator, second is denominator:
25755         10: {
25756             getValue: function (dataView, dataOffset, littleEndian) {
25757                 return dataView.getInt32(dataOffset, littleEndian) /
25758                     dataView.getInt32(dataOffset + 4, littleEndian);
25759             },
25760             size: 8
25761         }
25762     },
25763     
25764     footer : {
25765         STANDARD : [
25766             {
25767                 tag : 'div',
25768                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25769                 action : 'rotate-left',
25770                 cn : [
25771                     {
25772                         tag : 'button',
25773                         cls : 'btn btn-default',
25774                         html : '<i class="fa fa-undo"></i>'
25775                     }
25776                 ]
25777             },
25778             {
25779                 tag : 'div',
25780                 cls : 'btn-group roo-upload-cropbox-picture',
25781                 action : 'picture',
25782                 cn : [
25783                     {
25784                         tag : 'button',
25785                         cls : 'btn btn-default',
25786                         html : '<i class="fa fa-picture-o"></i>'
25787                     }
25788                 ]
25789             },
25790             {
25791                 tag : 'div',
25792                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25793                 action : 'rotate-right',
25794                 cn : [
25795                     {
25796                         tag : 'button',
25797                         cls : 'btn btn-default',
25798                         html : '<i class="fa fa-repeat"></i>'
25799                     }
25800                 ]
25801             }
25802         ],
25803         DOCUMENT : [
25804             {
25805                 tag : 'div',
25806                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25807                 action : 'rotate-left',
25808                 cn : [
25809                     {
25810                         tag : 'button',
25811                         cls : 'btn btn-default',
25812                         html : '<i class="fa fa-undo"></i>'
25813                     }
25814                 ]
25815             },
25816             {
25817                 tag : 'div',
25818                 cls : 'btn-group roo-upload-cropbox-download',
25819                 action : 'download',
25820                 cn : [
25821                     {
25822                         tag : 'button',
25823                         cls : 'btn btn-default',
25824                         html : '<i class="fa fa-download"></i>'
25825                     }
25826                 ]
25827             },
25828             {
25829                 tag : 'div',
25830                 cls : 'btn-group roo-upload-cropbox-crop',
25831                 action : 'crop',
25832                 cn : [
25833                     {
25834                         tag : 'button',
25835                         cls : 'btn btn-default',
25836                         html : '<i class="fa fa-crop"></i>'
25837                     }
25838                 ]
25839             },
25840             {
25841                 tag : 'div',
25842                 cls : 'btn-group roo-upload-cropbox-trash',
25843                 action : 'trash',
25844                 cn : [
25845                     {
25846                         tag : 'button',
25847                         cls : 'btn btn-default',
25848                         html : '<i class="fa fa-trash"></i>'
25849                     }
25850                 ]
25851             },
25852             {
25853                 tag : 'div',
25854                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25855                 action : 'rotate-right',
25856                 cn : [
25857                     {
25858                         tag : 'button',
25859                         cls : 'btn btn-default',
25860                         html : '<i class="fa fa-repeat"></i>'
25861                     }
25862                 ]
25863             }
25864         ],
25865         ROTATOR : [
25866             {
25867                 tag : 'div',
25868                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25869                 action : 'rotate-left',
25870                 cn : [
25871                     {
25872                         tag : 'button',
25873                         cls : 'btn btn-default',
25874                         html : '<i class="fa fa-undo"></i>'
25875                     }
25876                 ]
25877             },
25878             {
25879                 tag : 'div',
25880                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25881                 action : 'rotate-right',
25882                 cn : [
25883                     {
25884                         tag : 'button',
25885                         cls : 'btn btn-default',
25886                         html : '<i class="fa fa-repeat"></i>'
25887                     }
25888                 ]
25889             }
25890         ]
25891     }
25892 });
25893
25894 /*
25895 * Licence: LGPL
25896 */
25897
25898 /**
25899  * @class Roo.bootstrap.DocumentManager
25900  * @extends Roo.bootstrap.Component
25901  * Bootstrap DocumentManager class
25902  * @cfg {String} paramName default 'imageUpload'
25903  * @cfg {String} method default POST
25904  * @cfg {String} url action url
25905  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
25906  * @cfg {Boolean} multiple multiple upload default true
25907  * @cfg {Number} thumbSize default 300
25908  * @cfg {String} fieldLabel
25909  * @cfg {Number} labelWidth default 4
25910  * @cfg {String} labelAlign (left|top) default left
25911  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
25912  * 
25913  * @constructor
25914  * Create a new DocumentManager
25915  * @param {Object} config The config object
25916  */
25917
25918 Roo.bootstrap.DocumentManager = function(config){
25919     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25920     
25921     this.addEvents({
25922         /**
25923          * @event initial
25924          * Fire when initial the DocumentManager
25925          * @param {Roo.bootstrap.DocumentManager} this
25926          */
25927         "initial" : true,
25928         /**
25929          * @event inspect
25930          * inspect selected file
25931          * @param {Roo.bootstrap.DocumentManager} this
25932          * @param {File} file
25933          */
25934         "inspect" : true,
25935         /**
25936          * @event exception
25937          * Fire when xhr load exception
25938          * @param {Roo.bootstrap.DocumentManager} this
25939          * @param {XMLHttpRequest} xhr
25940          */
25941         "exception" : true,
25942         /**
25943          * @event prepare
25944          * prepare the form data
25945          * @param {Roo.bootstrap.DocumentManager} this
25946          * @param {Object} formData
25947          */
25948         "prepare" : true,
25949         /**
25950          * @event remove
25951          * Fire when remove the file
25952          * @param {Roo.bootstrap.DocumentManager} this
25953          * @param {Object} file
25954          */
25955         "remove" : true,
25956         /**
25957          * @event refresh
25958          * Fire after refresh the file
25959          * @param {Roo.bootstrap.DocumentManager} this
25960          */
25961         "refresh" : true,
25962         /**
25963          * @event click
25964          * Fire after click the image
25965          * @param {Roo.bootstrap.DocumentManager} this
25966          * @param {Object} file
25967          */
25968         "click" : true,
25969         /**
25970          * @event edit
25971          * Fire when upload a image and editable set to true
25972          * @param {Roo.bootstrap.DocumentManager} this
25973          * @param {Object} file
25974          */
25975         "edit" : true,
25976         /**
25977          * @event beforeselectfile
25978          * Fire before select file
25979          * @param {Roo.bootstrap.DocumentManager} this
25980          */
25981         "beforeselectfile" : true,
25982         /**
25983          * @event process
25984          * Fire before process file
25985          * @param {Roo.bootstrap.DocumentManager} this
25986          * @param {Object} file
25987          */
25988         "process" : true
25989         
25990     });
25991 };
25992
25993 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
25994     
25995     boxes : 0,
25996     inputName : '',
25997     thumbSize : 300,
25998     multiple : true,
25999     files : [],
26000     method : 'POST',
26001     url : '',
26002     paramName : 'imageUpload',
26003     fieldLabel : '',
26004     labelWidth : 4,
26005     labelAlign : 'left',
26006     editable : true,
26007     delegates : [],
26008     
26009     getAutoCreate : function()
26010     {   
26011         var managerWidget = {
26012             tag : 'div',
26013             cls : 'roo-document-manager',
26014             cn : [
26015                 {
26016                     tag : 'input',
26017                     cls : 'roo-document-manager-selector',
26018                     type : 'file'
26019                 },
26020                 {
26021                     tag : 'div',
26022                     cls : 'roo-document-manager-uploader',
26023                     cn : [
26024                         {
26025                             tag : 'div',
26026                             cls : 'roo-document-manager-upload-btn',
26027                             html : '<i class="fa fa-plus"></i>'
26028                         }
26029                     ]
26030                     
26031                 }
26032             ]
26033         };
26034         
26035         var content = [
26036             {
26037                 tag : 'div',
26038                 cls : 'column col-md-12',
26039                 cn : managerWidget
26040             }
26041         ];
26042         
26043         if(this.fieldLabel.length){
26044             
26045             content = [
26046                 {
26047                     tag : 'div',
26048                     cls : 'column col-md-12',
26049                     html : this.fieldLabel
26050                 },
26051                 {
26052                     tag : 'div',
26053                     cls : 'column col-md-12',
26054                     cn : managerWidget
26055                 }
26056             ];
26057
26058             if(this.labelAlign == 'left'){
26059                 content = [
26060                     {
26061                         tag : 'div',
26062                         cls : 'column col-md-' + this.labelWidth,
26063                         html : this.fieldLabel
26064                     },
26065                     {
26066                         tag : 'div',
26067                         cls : 'column col-md-' + (12 - this.labelWidth),
26068                         cn : managerWidget
26069                     }
26070                 ];
26071                 
26072             }
26073         }
26074         
26075         var cfg = {
26076             tag : 'div',
26077             cls : 'row clearfix',
26078             cn : content
26079         };
26080         
26081         return cfg;
26082         
26083     },
26084     
26085     initEvents : function()
26086     {
26087         this.managerEl = this.el.select('.roo-document-manager', true).first();
26088         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26089         
26090         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26091         this.selectorEl.hide();
26092         
26093         if(this.multiple){
26094             this.selectorEl.attr('multiple', 'multiple');
26095         }
26096         
26097         this.selectorEl.on('change', this.onFileSelected, this);
26098         
26099         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26100         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26101         
26102         this.uploader.on('click', this.onUploaderClick, this);
26103         
26104         this.renderProgressDialog();
26105         
26106         var _this = this;
26107         
26108         window.addEventListener("resize", function() { _this.refresh(); } );
26109         
26110         this.fireEvent('initial', this);
26111     },
26112     
26113     renderProgressDialog : function()
26114     {
26115         var _this = this;
26116         
26117         this.progressDialog = new Roo.bootstrap.Modal({
26118             cls : 'roo-document-manager-progress-dialog',
26119             allow_close : false,
26120             title : '',
26121             buttons : [
26122                 {
26123                     name  :'cancel',
26124                     weight : 'danger',
26125                     html : 'Cancel'
26126                 }
26127             ], 
26128             listeners : { 
26129                 btnclick : function() {
26130                     _this.uploadCancel();
26131                     this.hide();
26132                 }
26133             }
26134         });
26135          
26136         this.progressDialog.render(Roo.get(document.body));
26137          
26138         this.progress = new Roo.bootstrap.Progress({
26139             cls : 'roo-document-manager-progress',
26140             active : true,
26141             striped : true
26142         });
26143         
26144         this.progress.render(this.progressDialog.getChildContainer());
26145         
26146         this.progressBar = new Roo.bootstrap.ProgressBar({
26147             cls : 'roo-document-manager-progress-bar',
26148             aria_valuenow : 0,
26149             aria_valuemin : 0,
26150             aria_valuemax : 12,
26151             panel : 'success'
26152         });
26153         
26154         this.progressBar.render(this.progress.getChildContainer());
26155     },
26156     
26157     onUploaderClick : function(e)
26158     {
26159         e.preventDefault();
26160      
26161         if(this.fireEvent('beforeselectfile', this) != false){
26162             this.selectorEl.dom.click();
26163         }
26164         
26165     },
26166     
26167     onFileSelected : function(e)
26168     {
26169         e.preventDefault();
26170         
26171         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26172             return;
26173         }
26174         
26175         Roo.each(this.selectorEl.dom.files, function(file){
26176             if(this.fireEvent('inspect', this, file) != false){
26177                 this.files.push(file);
26178             }
26179         }, this);
26180         
26181         this.queue();
26182         
26183     },
26184     
26185     queue : function()
26186     {
26187         this.selectorEl.dom.value = '';
26188         
26189         if(!this.files.length){
26190             return;
26191         }
26192         
26193         if(this.boxes > 0 && this.files.length > this.boxes){
26194             this.files = this.files.slice(0, this.boxes);
26195         }
26196         
26197         this.uploader.show();
26198         
26199         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26200             this.uploader.hide();
26201         }
26202         
26203         var _this = this;
26204         
26205         var files = [];
26206         
26207         var docs = [];
26208         
26209         Roo.each(this.files, function(file){
26210             
26211             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26212                 var f = this.renderPreview(file);
26213                 files.push(f);
26214                 return;
26215             }
26216             
26217             if(file.type.indexOf('image') != -1){
26218                 this.delegates.push(
26219                     (function(){
26220                         _this.process(file);
26221                     }).createDelegate(this)
26222                 );
26223         
26224                 return;
26225             }
26226             
26227             docs.push(
26228                 (function(){
26229                     _this.process(file);
26230                 }).createDelegate(this)
26231             );
26232             
26233         }, this);
26234         
26235         this.files = files;
26236         
26237         this.delegates = this.delegates.concat(docs);
26238         
26239         if(!this.delegates.length){
26240             this.refresh();
26241             return;
26242         }
26243         
26244         this.progressBar.aria_valuemax = this.delegates.length;
26245         
26246         this.arrange();
26247         
26248         return;
26249     },
26250     
26251     arrange : function()
26252     {
26253         if(!this.delegates.length){
26254             this.progressDialog.hide();
26255             this.refresh();
26256             return;
26257         }
26258         
26259         var delegate = this.delegates.shift();
26260         
26261         this.progressDialog.show();
26262         
26263         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26264         
26265         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26266         
26267         delegate();
26268     },
26269     
26270     refresh : function()
26271     {
26272         this.uploader.show();
26273         
26274         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26275             this.uploader.hide();
26276         }
26277         
26278         Roo.isTouch ? this.closable(false) : this.closable(true);
26279         
26280         this.fireEvent('refresh', this);
26281     },
26282     
26283     onRemove : function(e, el, o)
26284     {
26285         e.preventDefault();
26286         
26287         this.fireEvent('remove', this, o);
26288         
26289     },
26290     
26291     remove : function(o)
26292     {
26293         var files = [];
26294         
26295         Roo.each(this.files, function(file){
26296             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26297                 files.push(file);
26298                 return;
26299             }
26300
26301             o.target.remove();
26302
26303         }, this);
26304         
26305         this.files = files;
26306         
26307         this.refresh();
26308     },
26309     
26310     clear : function()
26311     {
26312         Roo.each(this.files, function(file){
26313             if(!file.target){
26314                 return;
26315             }
26316             
26317             file.target.remove();
26318
26319         }, this);
26320         
26321         this.files = [];
26322         
26323         this.refresh();
26324     },
26325     
26326     onClick : function(e, el, o)
26327     {
26328         e.preventDefault();
26329         
26330         this.fireEvent('click', this, o);
26331         
26332     },
26333     
26334     closable : function(closable)
26335     {
26336         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26337             
26338             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26339             
26340             if(closable){
26341                 el.show();
26342                 return;
26343             }
26344             
26345             el.hide();
26346             
26347         }, this);
26348     },
26349     
26350     xhrOnLoad : function(xhr)
26351     {
26352         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26353             el.remove();
26354         }, this);
26355         
26356         if (xhr.readyState !== 4) {
26357             this.arrange();
26358             this.fireEvent('exception', this, xhr);
26359             return;
26360         }
26361
26362         var response = Roo.decode(xhr.responseText);
26363         
26364         if(!response.success){
26365             this.arrange();
26366             this.fireEvent('exception', this, xhr);
26367             return;
26368         }
26369         
26370         var file = this.renderPreview(response.data);
26371         
26372         this.files.push(file);
26373         
26374         this.arrange();
26375         
26376     },
26377     
26378     xhrOnError : function()
26379     {
26380         Roo.log('xhr on error');
26381         
26382         var response = Roo.decode(xhr.responseText);
26383           
26384         Roo.log(response);
26385         
26386         this.arrange();
26387     },
26388     
26389     process : function(file)
26390     {
26391         if(this.fireEvent('process', this, file) !== false){
26392             if(this.editable && file.type.indexOf('image') != -1){
26393                 this.fireEvent('edit', this, file);
26394                 return;
26395             }
26396
26397             this.uploadStart(file, false);
26398
26399             return;
26400         }
26401         
26402     },
26403     
26404     uploadStart : function(file, crop)
26405     {
26406         this.xhr = new XMLHttpRequest();
26407         
26408         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26409             this.arrange();
26410             return;
26411         }
26412         
26413         file.xhr = this.xhr;
26414             
26415         this.managerEl.createChild({
26416             tag : 'div',
26417             cls : 'roo-document-manager-loading',
26418             cn : [
26419                 {
26420                     tag : 'div',
26421                     tooltip : file.name,
26422                     cls : 'roo-document-manager-thumb',
26423                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26424                 }
26425             ]
26426
26427         });
26428
26429         this.xhr.open(this.method, this.url, true);
26430         
26431         var headers = {
26432             "Accept": "application/json",
26433             "Cache-Control": "no-cache",
26434             "X-Requested-With": "XMLHttpRequest"
26435         };
26436         
26437         for (var headerName in headers) {
26438             var headerValue = headers[headerName];
26439             if (headerValue) {
26440                 this.xhr.setRequestHeader(headerName, headerValue);
26441             }
26442         }
26443         
26444         var _this = this;
26445         
26446         this.xhr.onload = function()
26447         {
26448             _this.xhrOnLoad(_this.xhr);
26449         }
26450         
26451         this.xhr.onerror = function()
26452         {
26453             _this.xhrOnError(_this.xhr);
26454         }
26455         
26456         var formData = new FormData();
26457
26458         formData.append('returnHTML', 'NO');
26459         
26460         if(crop){
26461             formData.append('crop', crop);
26462         }
26463         
26464         formData.append(this.paramName, file, file.name);
26465         
26466         if(this.fireEvent('prepare', this, formData) != false){
26467             this.xhr.send(formData);
26468         };
26469     },
26470     
26471     uploadCancel : function()
26472     {
26473         this.xhr.abort();
26474         
26475         this.delegates = [];
26476         
26477         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26478             el.remove();
26479         }, this);
26480         
26481         this.arrange();
26482     },
26483     
26484     renderPreview : function(file)
26485     {
26486         if(typeof(file.target) != 'undefined' && file.target){
26487             return file;
26488         }
26489         
26490         var previewEl = this.managerEl.createChild({
26491             tag : 'div',
26492             cls : 'roo-document-manager-preview',
26493             cn : [
26494                 {
26495                     tag : 'div',
26496                     tooltip : file.filename,
26497                     cls : 'roo-document-manager-thumb',
26498                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26499                 },
26500                 {
26501                     tag : 'button',
26502                     cls : 'close',
26503                     html : '<i class="fa fa-times-circle"></i>'
26504                 }
26505             ]
26506         });
26507
26508         var close = previewEl.select('button.close', true).first();
26509
26510         close.on('click', this.onRemove, this, file);
26511
26512         file.target = previewEl;
26513
26514         var image = previewEl.select('img', true).first();
26515         
26516         var _this = this;
26517         
26518         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26519         
26520         image.on('click', this.onClick, this, file);
26521         
26522         return file;
26523         
26524     },
26525     
26526     onPreviewLoad : function(file, image)
26527     {
26528         if(typeof(file.target) == 'undefined' || !file.target){
26529             return;
26530         }
26531         
26532         var width = image.dom.naturalWidth || image.dom.width;
26533         var height = image.dom.naturalHeight || image.dom.height;
26534         
26535         if(width > height){
26536             file.target.addClass('wide');
26537             return;
26538         }
26539         
26540         file.target.addClass('tall');
26541         return;
26542         
26543     },
26544     
26545     uploadFromSource : function(file, crop)
26546     {
26547         this.xhr = new XMLHttpRequest();
26548         
26549         this.managerEl.createChild({
26550             tag : 'div',
26551             cls : 'roo-document-manager-loading',
26552             cn : [
26553                 {
26554                     tag : 'div',
26555                     tooltip : file.name,
26556                     cls : 'roo-document-manager-thumb',
26557                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26558                 }
26559             ]
26560
26561         });
26562
26563         this.xhr.open(this.method, this.url, true);
26564         
26565         var headers = {
26566             "Accept": "application/json",
26567             "Cache-Control": "no-cache",
26568             "X-Requested-With": "XMLHttpRequest"
26569         };
26570         
26571         for (var headerName in headers) {
26572             var headerValue = headers[headerName];
26573             if (headerValue) {
26574                 this.xhr.setRequestHeader(headerName, headerValue);
26575             }
26576         }
26577         
26578         var _this = this;
26579         
26580         this.xhr.onload = function()
26581         {
26582             _this.xhrOnLoad(_this.xhr);
26583         }
26584         
26585         this.xhr.onerror = function()
26586         {
26587             _this.xhrOnError(_this.xhr);
26588         }
26589         
26590         var formData = new FormData();
26591
26592         formData.append('returnHTML', 'NO');
26593         
26594         formData.append('crop', crop);
26595         
26596         if(typeof(file.filename) != 'undefined'){
26597             formData.append('filename', file.filename);
26598         }
26599         
26600         if(typeof(file.mimetype) != 'undefined'){
26601             formData.append('mimetype', file.mimetype);
26602         }
26603         
26604         if(this.fireEvent('prepare', this, formData) != false){
26605             this.xhr.send(formData);
26606         };
26607     }
26608 });
26609
26610 /*
26611 * Licence: LGPL
26612 */
26613
26614 /**
26615  * @class Roo.bootstrap.DocumentViewer
26616  * @extends Roo.bootstrap.Component
26617  * Bootstrap DocumentViewer class
26618  * 
26619  * @constructor
26620  * Create a new DocumentViewer
26621  * @param {Object} config The config object
26622  */
26623
26624 Roo.bootstrap.DocumentViewer = function(config){
26625     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26626     
26627     this.addEvents({
26628         /**
26629          * @event initial
26630          * Fire after initEvent
26631          * @param {Roo.bootstrap.DocumentViewer} this
26632          */
26633         "initial" : true,
26634         /**
26635          * @event click
26636          * Fire after click
26637          * @param {Roo.bootstrap.DocumentViewer} this
26638          */
26639         "click" : true,
26640         /**
26641          * @event trash
26642          * Fire after trash button
26643          * @param {Roo.bootstrap.DocumentViewer} this
26644          */
26645         "trash" : true
26646         
26647     });
26648 };
26649
26650 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
26651     
26652     getAutoCreate : function()
26653     {
26654         var cfg = {
26655             tag : 'div',
26656             cls : 'roo-document-viewer',
26657             cn : [
26658                 {
26659                     tag : 'div',
26660                     cls : 'roo-document-viewer-body',
26661                     cn : [
26662                         {
26663                             tag : 'div',
26664                             cls : 'roo-document-viewer-thumb',
26665                             cn : [
26666                                 {
26667                                     tag : 'img',
26668                                     cls : 'roo-document-viewer-image'
26669                                 }
26670                             ]
26671                         }
26672                     ]
26673                 },
26674                 {
26675                     tag : 'div',
26676                     cls : 'roo-document-viewer-footer',
26677                     cn : {
26678                         tag : 'div',
26679                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26680                         cn : [
26681                             {
26682                                 tag : 'div',
26683                                 cls : 'btn-group',
26684                                 cn : [
26685                                     {
26686                                         tag : 'button',
26687                                         cls : 'btn btn-default roo-document-viewer-trash',
26688                                         html : '<i class="fa fa-trash"></i>'
26689                                     }
26690                                 ]
26691                             }
26692                         ]
26693                     }
26694                 }
26695             ]
26696         };
26697         
26698         return cfg;
26699     },
26700     
26701     initEvents : function()
26702     {
26703         
26704         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26705         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26706         
26707         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26708         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26709         
26710         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26711         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26712         
26713         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26714         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26715         
26716         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26717         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26718         
26719         this.bodyEl.on('click', this.onClick, this);
26720         
26721         this.trashBtn.on('click', this.onTrash, this);
26722         
26723     },
26724     
26725     initial : function()
26726     {
26727 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26728         
26729         
26730         this.fireEvent('initial', this);
26731         
26732     },
26733     
26734     onClick : function(e)
26735     {
26736         e.preventDefault();
26737         
26738         this.fireEvent('click', this);
26739     },
26740     
26741     onTrash : function(e)
26742     {
26743         e.preventDefault();
26744         
26745         this.fireEvent('trash', this);
26746     }
26747     
26748 });
26749 /*
26750  * - LGPL
26751  *
26752  * nav progress bar
26753  * 
26754  */
26755
26756 /**
26757  * @class Roo.bootstrap.NavProgressBar
26758  * @extends Roo.bootstrap.Component
26759  * Bootstrap NavProgressBar class
26760  * 
26761  * @constructor
26762  * Create a new nav progress bar
26763  * @param {Object} config The config object
26764  */
26765
26766 Roo.bootstrap.NavProgressBar = function(config){
26767     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26768
26769     this.bullets = this.bullets || [];
26770    
26771 //    Roo.bootstrap.NavProgressBar.register(this);
26772      this.addEvents({
26773         /**
26774              * @event changed
26775              * Fires when the active item changes
26776              * @param {Roo.bootstrap.NavProgressBar} this
26777              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26778              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
26779          */
26780         'changed': true
26781      });
26782     
26783 };
26784
26785 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
26786     
26787     bullets : [],
26788     barItems : [],
26789     
26790     getAutoCreate : function()
26791     {
26792         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26793         
26794         cfg = {
26795             tag : 'div',
26796             cls : 'roo-navigation-bar-group',
26797             cn : [
26798                 {
26799                     tag : 'div',
26800                     cls : 'roo-navigation-top-bar'
26801                 },
26802                 {
26803                     tag : 'div',
26804                     cls : 'roo-navigation-bullets-bar',
26805                     cn : [
26806                         {
26807                             tag : 'ul',
26808                             cls : 'roo-navigation-bar'
26809                         }
26810                     ]
26811                 },
26812                 
26813                 {
26814                     tag : 'div',
26815                     cls : 'roo-navigation-bottom-bar'
26816                 }
26817             ]
26818             
26819         };
26820         
26821         return cfg;
26822         
26823     },
26824     
26825     initEvents: function() 
26826     {
26827         
26828     },
26829     
26830     onRender : function(ct, position) 
26831     {
26832         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26833         
26834         if(this.bullets.length){
26835             Roo.each(this.bullets, function(b){
26836                this.addItem(b);
26837             }, this);
26838         }
26839         
26840         this.format();
26841         
26842     },
26843     
26844     addItem : function(cfg)
26845     {
26846         var item = new Roo.bootstrap.NavProgressItem(cfg);
26847         
26848         item.parentId = this.id;
26849         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
26850         
26851         if(cfg.html){
26852             var top = new Roo.bootstrap.Element({
26853                 tag : 'div',
26854                 cls : 'roo-navigation-bar-text'
26855             });
26856             
26857             var bottom = new Roo.bootstrap.Element({
26858                 tag : 'div',
26859                 cls : 'roo-navigation-bar-text'
26860             });
26861             
26862             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
26863             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
26864             
26865             var topText = new Roo.bootstrap.Element({
26866                 tag : 'span',
26867                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
26868             });
26869             
26870             var bottomText = new Roo.bootstrap.Element({
26871                 tag : 'span',
26872                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
26873             });
26874             
26875             topText.onRender(top.el, null);
26876             bottomText.onRender(bottom.el, null);
26877             
26878             item.topEl = top;
26879             item.bottomEl = bottom;
26880         }
26881         
26882         this.barItems.push(item);
26883         
26884         return item;
26885     },
26886     
26887     getActive : function()
26888     {
26889         var active = false;
26890         
26891         Roo.each(this.barItems, function(v){
26892             
26893             if (!v.isActive()) {
26894                 return;
26895             }
26896             
26897             active = v;
26898             return false;
26899             
26900         });
26901         
26902         return active;
26903     },
26904     
26905     setActiveItem : function(item)
26906     {
26907         var prev = false;
26908         
26909         Roo.each(this.barItems, function(v){
26910             if (v.rid == item.rid) {
26911                 return ;
26912             }
26913             
26914             if (v.isActive()) {
26915                 v.setActive(false);
26916                 prev = v;
26917             }
26918         });
26919
26920         item.setActive(true);
26921         
26922         this.fireEvent('changed', this, item, prev);
26923     },
26924     
26925     getBarItem: function(rid)
26926     {
26927         var ret = false;
26928         
26929         Roo.each(this.barItems, function(e) {
26930             if (e.rid != rid) {
26931                 return;
26932             }
26933             
26934             ret =  e;
26935             return false;
26936         });
26937         
26938         return ret;
26939     },
26940     
26941     indexOfItem : function(item)
26942     {
26943         var index = false;
26944         
26945         Roo.each(this.barItems, function(v, i){
26946             
26947             if (v.rid != item.rid) {
26948                 return;
26949             }
26950             
26951             index = i;
26952             return false
26953         });
26954         
26955         return index;
26956     },
26957     
26958     setActiveNext : function()
26959     {
26960         var i = this.indexOfItem(this.getActive());
26961         
26962         if (i > this.barItems.length) {
26963             return;
26964         }
26965         
26966         this.setActiveItem(this.barItems[i+1]);
26967     },
26968     
26969     setActivePrev : function()
26970     {
26971         var i = this.indexOfItem(this.getActive());
26972         
26973         if (i  < 1) {
26974             return;
26975         }
26976         
26977         this.setActiveItem(this.barItems[i-1]);
26978     },
26979     
26980     format : function()
26981     {
26982         if(!this.barItems.length){
26983             return;
26984         }
26985      
26986         var width = 100 / this.barItems.length;
26987         
26988         Roo.each(this.barItems, function(i){
26989             i.el.setStyle('width', width + '%');
26990             i.topEl.el.setStyle('width', width + '%');
26991             i.bottomEl.el.setStyle('width', width + '%');
26992         }, this);
26993         
26994     }
26995     
26996 });
26997 /*
26998  * - LGPL
26999  *
27000  * Nav Progress Item
27001  * 
27002  */
27003
27004 /**
27005  * @class Roo.bootstrap.NavProgressItem
27006  * @extends Roo.bootstrap.Component
27007  * Bootstrap NavProgressItem class
27008  * @cfg {String} rid the reference id
27009  * @cfg {Boolean} active (true|false) Is item active default false
27010  * @cfg {Boolean} disabled (true|false) Is item active default false
27011  * @cfg {String} html
27012  * @cfg {String} position (top|bottom) text position default bottom
27013  * @cfg {String} icon show icon instead of number
27014  * 
27015  * @constructor
27016  * Create a new NavProgressItem
27017  * @param {Object} config The config object
27018  */
27019 Roo.bootstrap.NavProgressItem = function(config){
27020     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27021     this.addEvents({
27022         // raw events
27023         /**
27024          * @event click
27025          * The raw click event for the entire grid.
27026          * @param {Roo.bootstrap.NavProgressItem} this
27027          * @param {Roo.EventObject} e
27028          */
27029         "click" : true
27030     });
27031    
27032 };
27033
27034 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27035     
27036     rid : '',
27037     active : false,
27038     disabled : false,
27039     html : '',
27040     position : 'bottom',
27041     icon : false,
27042     
27043     getAutoCreate : function()
27044     {
27045         var iconCls = 'roo-navigation-bar-item-icon';
27046         
27047         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27048         
27049         var cfg = {
27050             tag: 'li',
27051             cls: 'roo-navigation-bar-item',
27052             cn : [
27053                 {
27054                     tag : 'i',
27055                     cls : iconCls
27056                 }
27057             ]
27058         }
27059         
27060         if(this.active){
27061             cfg.cls += ' active';
27062         }
27063         if(this.disabled){
27064             cfg.cls += ' disabled';
27065         }
27066         
27067         return cfg;
27068     },
27069     
27070     disable : function()
27071     {
27072         this.setDisabled(true);
27073     },
27074     
27075     enable : function()
27076     {
27077         this.setDisabled(false);
27078     },
27079     
27080     initEvents: function() 
27081     {
27082         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27083         
27084         this.iconEl.on('click', this.onClick, this);
27085     },
27086     
27087     onClick : function(e)
27088     {
27089         e.preventDefault();
27090         
27091         if(this.disabled){
27092             return;
27093         }
27094         
27095         if(this.fireEvent('click', this, e) === false){
27096             return;
27097         };
27098         
27099         this.parent().setActiveItem(this);
27100     },
27101     
27102     isActive: function () 
27103     {
27104         return this.active;
27105     },
27106     
27107     setActive : function(state)
27108     {
27109         if(this.active == state){
27110             return;
27111         }
27112         
27113         this.active = state;
27114         
27115         if (state) {
27116             this.el.addClass('active');
27117             return;
27118         }
27119         
27120         this.el.removeClass('active');
27121         
27122         return;
27123     },
27124     
27125     setDisabled : function(state)
27126     {
27127         if(this.disabled == state){
27128             return;
27129         }
27130         
27131         this.disabled = state;
27132         
27133         if (state) {
27134             this.el.addClass('disabled');
27135             return;
27136         }
27137         
27138         this.el.removeClass('disabled');
27139     },
27140     
27141     tooltipEl : function()
27142     {
27143         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27144     }
27145 });
27146  
27147
27148