Date.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         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 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         
1969         
1970         
1971         
1972         if (Roo.isTouch) {
1973             this.el.on('touchstart'  , this.onTouch, this);
1974         }
1975         this.el.on('click' , this.onClick, this);
1976
1977         this.el.on("mouseover", this.onMouseOver, this);
1978         this.el.on("mouseout", this.onMouseOut, this);
1979         
1980         
1981     },
1982     findTargetItem : function(e){
1983         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1984         if(!t){
1985             return false;
1986         }
1987         //Roo.log(t);         Roo.log(t.id);
1988         if(t && t.id){
1989             //Roo.log(this.menuitems);
1990             return this.menuitems.get(t.id);
1991             
1992             //return this.items.get(t.menuItemId);
1993         }
1994         
1995         return false;
1996     },
1997     
1998     onTouch : function(e) {
1999         e.stopEvent();
2000         this.onClick(e);
2001     },
2002     
2003     onClick : function(e){
2004         Roo.log("menu.onClick");
2005         var t = this.findTargetItem(e);
2006         if(!t || t.isContainer){
2007             return;
2008         }
2009         Roo.log(e);
2010         /*
2011         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2012             if(t == this.activeItem && t.shouldDeactivate(e)){
2013                 this.activeItem.deactivate();
2014                 delete this.activeItem;
2015                 return;
2016             }
2017             if(t.canActivate){
2018                 this.setActiveItem(t, true);
2019             }
2020             return;
2021             
2022             
2023         }
2024         */
2025        
2026         Roo.log('pass click event');
2027         
2028         t.onClick(e);
2029         
2030         this.fireEvent("click", this, t, e);
2031         
2032         this.hide();
2033     },
2034      onMouseOver : function(e){
2035         var t  = this.findTargetItem(e);
2036         //Roo.log(t);
2037         //if(t){
2038         //    if(t.canActivate && !t.disabled){
2039         //        this.setActiveItem(t, true);
2040         //    }
2041         //}
2042         
2043         this.fireEvent("mouseover", this, e, t);
2044     },
2045     isVisible : function(){
2046         return !this.hidden;
2047     },
2048      onMouseOut : function(e){
2049         var t  = this.findTargetItem(e);
2050         
2051         //if(t ){
2052         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2053         //        this.activeItem.deactivate();
2054         //        delete this.activeItem;
2055         //    }
2056         //}
2057         this.fireEvent("mouseout", this, e, t);
2058     },
2059     
2060     
2061     /**
2062      * Displays this menu relative to another element
2063      * @param {String/HTMLElement/Roo.Element} element The element to align to
2064      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2065      * the element (defaults to this.defaultAlign)
2066      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2067      */
2068     show : function(el, pos, parentMenu){
2069         this.parentMenu = parentMenu;
2070         if(!this.el){
2071             this.render();
2072         }
2073         this.fireEvent("beforeshow", this);
2074         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2075     },
2076      /**
2077      * Displays this menu at a specific xy position
2078      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2079      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2080      */
2081     showAt : function(xy, parentMenu, /* private: */_e){
2082         this.parentMenu = parentMenu;
2083         if(!this.el){
2084             this.render();
2085         }
2086         if(_e !== false){
2087             this.fireEvent("beforeshow", this);
2088             //xy = this.el.adjustForConstraints(xy);
2089         }
2090         
2091         //this.el.show();
2092         this.hideMenuItems();
2093         this.hidden = false;
2094         this.triggerEl.addClass('open');
2095         
2096         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2097             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2098         }
2099         
2100         this.el.setXY(xy);
2101         this.focus();
2102         this.fireEvent("show", this);
2103     },
2104     
2105     focus : function(){
2106         return;
2107         if(!this.hidden){
2108             this.doFocus.defer(50, this);
2109         }
2110     },
2111
2112     doFocus : function(){
2113         if(!this.hidden){
2114             this.focusEl.focus();
2115         }
2116     },
2117
2118     /**
2119      * Hides this menu and optionally all parent menus
2120      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2121      */
2122     hide : function(deep){
2123         
2124         this.hideMenuItems();
2125         if(this.el && this.isVisible()){
2126             this.fireEvent("beforehide", this);
2127             if(this.activeItem){
2128                 this.activeItem.deactivate();
2129                 this.activeItem = null;
2130             }
2131             this.triggerEl.removeClass('open');;
2132             this.hidden = true;
2133             this.fireEvent("hide", this);
2134         }
2135         if(deep === true && this.parentMenu){
2136             this.parentMenu.hide(true);
2137         }
2138     },
2139     
2140     onTriggerPress  : function(e)
2141     {
2142         
2143         Roo.log('trigger press');
2144         //Roo.log(e.getTarget());
2145        // Roo.log(this.triggerEl.dom);
2146         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2147             return;
2148         }
2149         
2150         if (this.isVisible()) {
2151             Roo.log('hide');
2152             this.hide();
2153         } else {
2154             Roo.log('show');
2155             this.show(this.triggerEl, false, false);
2156         }
2157         
2158         e.stopEvent();
2159     },
2160     
2161          
2162        
2163     
2164     hideMenuItems : function()
2165     {
2166         //$(backdrop).remove()
2167         Roo.select('.open',true).each(function(aa) {
2168             
2169             aa.removeClass('open');
2170           //var parent = getParent($(this))
2171           //var relatedTarget = { relatedTarget: this }
2172           
2173            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2174           //if (e.isDefaultPrevented()) return
2175            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2176         })
2177     },
2178     addxtypeChild : function (tree, cntr) {
2179         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2180           
2181         this.menuitems.add(comp);
2182         return comp;
2183
2184     },
2185     getEl : function()
2186     {
2187         Roo.log(this.el);
2188         return this.el;
2189     }
2190 });
2191
2192  
2193  /*
2194  * - LGPL
2195  *
2196  * menu item
2197  * 
2198  */
2199
2200
2201 /**
2202  * @class Roo.bootstrap.MenuItem
2203  * @extends Roo.bootstrap.Component
2204  * Bootstrap MenuItem class
2205  * @cfg {String} html the menu label
2206  * @cfg {String} href the link
2207  * @cfg {Boolean} preventDefault (true | false) default true
2208  * @cfg {Boolean} isContainer (true | false) default false
2209  * 
2210  * 
2211  * @constructor
2212  * Create a new MenuItem
2213  * @param {Object} config The config object
2214  */
2215
2216
2217 Roo.bootstrap.MenuItem = function(config){
2218     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2219     this.addEvents({
2220         // raw events
2221         /**
2222          * @event click
2223          * The raw click event for the entire grid.
2224          * @param {Roo.bootstrap.MenuItem} this
2225          * @param {Roo.EventObject} e
2226          */
2227         "click" : true
2228     });
2229 };
2230
2231 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2232     
2233     href : false,
2234     html : false,
2235     preventDefault: true,
2236     isContainer : false,
2237     
2238     getAutoCreate : function(){
2239         
2240         if(this.isContainer){
2241             return {
2242                 tag: 'li',
2243                 cls: 'dropdown-menu-item'
2244             };
2245         }
2246         
2247         var cfg= {
2248             tag: 'li',
2249             cls: 'dropdown-menu-item',
2250             cn: [
2251                     {
2252                         tag : 'a',
2253                         href : '#',
2254                         html : 'Link'
2255                     }
2256                 ]
2257         };
2258         if (this.parent().type == 'treeview') {
2259             cfg.cls = 'treeview-menu';
2260         }
2261         
2262         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2263         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2264         return cfg;
2265     },
2266     
2267     initEvents: function() {
2268         
2269         //this.el.select('a').on('click', this.onClick, this);
2270         
2271     },
2272     onClick : function(e)
2273     {
2274         Roo.log('item on click ');
2275         //if(this.preventDefault){
2276         //    e.preventDefault();
2277         //}
2278         //this.parent().hideMenuItems();
2279         
2280         this.fireEvent('click', this, e);
2281     },
2282     getEl : function()
2283     {
2284         return this.el;
2285     }
2286 });
2287
2288  
2289
2290  /*
2291  * - LGPL
2292  *
2293  * menu separator
2294  * 
2295  */
2296
2297
2298 /**
2299  * @class Roo.bootstrap.MenuSeparator
2300  * @extends Roo.bootstrap.Component
2301  * Bootstrap MenuSeparator class
2302  * 
2303  * @constructor
2304  * Create a new MenuItem
2305  * @param {Object} config The config object
2306  */
2307
2308
2309 Roo.bootstrap.MenuSeparator = function(config){
2310     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2311 };
2312
2313 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2314     
2315     getAutoCreate : function(){
2316         var cfg = {
2317             cls: 'divider',
2318             tag : 'li'
2319         };
2320         
2321         return cfg;
2322     }
2323    
2324 });
2325
2326  
2327
2328  
2329 /*
2330 * Licence: LGPL
2331 */
2332
2333 /**
2334  * @class Roo.bootstrap.Modal
2335  * @extends Roo.bootstrap.Component
2336  * Bootstrap Modal class
2337  * @cfg {String} title Title of dialog
2338  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2339  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2340  * @cfg {Boolean} specificTitle default false
2341  * @cfg {Array} buttons Array of buttons or standard button set..
2342  * @cfg {String} buttonPosition (left|right|center) default right
2343  * @cfg {Boolean} animate default true
2344  * @cfg {Boolean} allow_close default true
2345  * 
2346  * @constructor
2347  * Create a new Modal Dialog
2348  * @param {Object} config The config object
2349  */
2350
2351 Roo.bootstrap.Modal = function(config){
2352     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2353     this.addEvents({
2354         // raw events
2355         /**
2356          * @event btnclick
2357          * The raw btnclick event for the button
2358          * @param {Roo.EventObject} e
2359          */
2360         "btnclick" : true
2361     });
2362     this.buttons = this.buttons || [];
2363      
2364     if (this.tmpl) {
2365         this.tmpl = Roo.factory(this.tmpl);
2366     }
2367     
2368 };
2369
2370 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2371     
2372     title : 'test dialog',
2373    
2374     buttons : false,
2375     
2376     // set on load...
2377      
2378     html: false,
2379     
2380     tmp: false,
2381     
2382     specificTitle: false,
2383     
2384     buttonPosition: 'right',
2385     
2386     allow_close : true,
2387     
2388     animate : true,
2389     
2390     
2391      // private
2392     bodyEl:  false,
2393     footerEl:  false,
2394     titleEl:  false,
2395     closeEl:  false,
2396     
2397     
2398     onRender : function(ct, position)
2399     {
2400         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2401      
2402         if(!this.el){
2403             var cfg = Roo.apply({},  this.getAutoCreate());
2404             cfg.id = Roo.id();
2405             //if(!cfg.name){
2406             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2407             //}
2408             //if (!cfg.name.length) {
2409             //    delete cfg.name;
2410            // }
2411             if (this.cls) {
2412                 cfg.cls += ' ' + this.cls;
2413             }
2414             if (this.style) {
2415                 cfg.style = this.style;
2416             }
2417             this.el = Roo.get(document.body).createChild(cfg, position);
2418         }
2419         //var type = this.el.dom.type;
2420         
2421         
2422         
2423         
2424         if(this.tabIndex !== undefined){
2425             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2426         }
2427         
2428         
2429         this.bodyEl = this.el.select('.modal-body',true).first();
2430         this.closeEl = this.el.select('.modal-header .close', true).first();
2431         this.footerEl = this.el.select('.modal-footer',true).first();
2432         this.titleEl = this.el.select('.modal-title',true).first();
2433         
2434         
2435          
2436         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2437         this.maskEl.enableDisplayMode("block");
2438         this.maskEl.hide();
2439         //this.el.addClass("x-dlg-modal");
2440     
2441         if (this.buttons.length) {
2442             Roo.each(this.buttons, function(bb) {
2443                 var b = Roo.apply({}, bb);
2444                 b.xns = b.xns || Roo.bootstrap;
2445                 b.xtype = b.xtype || 'Button';
2446                 if (typeof(b.listeners) == 'undefined') {
2447                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2448                 }
2449                 
2450                 var btn = Roo.factory(b);
2451                 
2452                 btn.onRender(this.el.select('.modal-footer div').first());
2453                 
2454             },this);
2455         }
2456         // render the children.
2457         var nitems = [];
2458         
2459         if(typeof(this.items) != 'undefined'){
2460             var items = this.items;
2461             delete this.items;
2462
2463             for(var i =0;i < items.length;i++) {
2464                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2465             }
2466         }
2467         
2468         this.items = nitems;
2469         
2470         // where are these used - they used to be body/close/footer
2471         
2472        
2473         this.initEvents();
2474         //this.el.addClass([this.fieldClass, this.cls]);
2475         
2476     },
2477     
2478     getAutoCreate : function(){
2479         
2480         
2481         var bdy = {
2482                 cls : 'modal-body',
2483                 html : this.html || ''
2484         };
2485         
2486         var title = {
2487             tag: 'h4',
2488             cls : 'modal-title',
2489             html : this.title
2490         };
2491         
2492         if(this.specificTitle){
2493             title = this.title;
2494             
2495         };
2496         
2497         var header = [];
2498         if (this.allow_close) {
2499             header.push({
2500                 tag: 'button',
2501                 cls : 'close',
2502                 html : '&times'
2503             });
2504         }
2505         header.push(title);
2506         
2507         var modal = {
2508             cls: "modal",
2509             style : 'display: none',
2510             cn : [
2511                 {
2512                     cls: "modal-dialog",
2513                     cn : [
2514                         {
2515                             cls : "modal-content",
2516                             cn : [
2517                                 {
2518                                     cls : 'modal-header',
2519                                     cn : header
2520                                 },
2521                                 bdy,
2522                                 {
2523                                     cls : 'modal-footer',
2524                                     cn : [
2525                                         {
2526                                             tag: 'div',
2527                                             cls: 'btn-' + this.buttonPosition
2528                                         }
2529                                     ]
2530                                     
2531                                 }
2532                                 
2533                                 
2534                             ]
2535                             
2536                         }
2537                     ]
2538                         
2539                 }
2540             ]
2541         };
2542         
2543         if(this.animate){
2544             modal.cls += ' fade';
2545         }
2546         
2547         return modal;
2548           
2549     },
2550     getChildContainer : function() {
2551          
2552          return this.bodyEl;
2553         
2554     },
2555     getButtonContainer : function() {
2556          return this.el.select('.modal-footer div',true).first();
2557         
2558     },
2559     initEvents : function()
2560     {
2561         if (this.allow_close) {
2562             this.closeEl.on('click', this.hide, this);
2563         }
2564         
2565         var _this = this;
2566         
2567         window.addEventListener("resize", function() { _this.resize(); } );
2568
2569     },
2570     
2571     resize : function()
2572     {
2573         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2574     },
2575     
2576     show : function() {
2577         
2578         if (!this.rendered) {
2579             this.render();
2580         }
2581         
2582         this.el.setStyle('display', 'block');
2583         
2584         if(this.animate){
2585             var _this = this;
2586             (function(){ _this.el.addClass('in'); }).defer(50);
2587         }else{
2588             this.el.addClass('in');
2589         }
2590         
2591         // not sure how we can show data in here.. 
2592         //if (this.tmpl) {
2593         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2594         //}
2595         
2596         Roo.get(document.body).addClass("x-body-masked");
2597         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2598         this.maskEl.show();
2599         this.el.setStyle('zIndex', '10001');
2600        
2601         this.fireEvent('show', this);
2602         
2603         
2604     },
2605     hide : function()
2606     {
2607         this.maskEl.hide();
2608         Roo.get(document.body).removeClass("x-body-masked");
2609         this.el.removeClass('in');
2610         
2611         if(this.animate){
2612             var _this = this;
2613             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2614         }else{
2615             this.el.setStyle('display', 'none');
2616         }
2617         
2618         this.fireEvent('hide', this);
2619     },
2620     
2621     addButton : function(str, cb)
2622     {
2623          
2624         
2625         var b = Roo.apply({}, { html : str } );
2626         b.xns = b.xns || Roo.bootstrap;
2627         b.xtype = b.xtype || 'Button';
2628         if (typeof(b.listeners) == 'undefined') {
2629             b.listeners = { click : cb.createDelegate(this)  };
2630         }
2631         
2632         var btn = Roo.factory(b);
2633            
2634         btn.onRender(this.el.select('.modal-footer div').first());
2635         
2636         return btn;   
2637        
2638     },
2639     
2640     setDefaultButton : function(btn)
2641     {
2642         //this.el.select('.modal-footer').()
2643     },
2644     resizeTo: function(w,h)
2645     {
2646         // skip..
2647     },
2648     setContentSize  : function(w, h)
2649     {
2650         
2651     },
2652     onButtonClick: function(btn,e)
2653     {
2654         //Roo.log([a,b,c]);
2655         this.fireEvent('btnclick', btn.name, e);
2656     },
2657      /**
2658      * Set the title of the Dialog
2659      * @param {String} str new Title
2660      */
2661     setTitle: function(str) {
2662         this.titleEl.dom.innerHTML = str;    
2663     },
2664     /**
2665      * Set the body of the Dialog
2666      * @param {String} str new Title
2667      */
2668     setBody: function(str) {
2669         this.bodyEl.dom.innerHTML = str;    
2670     },
2671     /**
2672      * Set the body of the Dialog using the template
2673      * @param {Obj} data - apply this data to the template and replace the body contents.
2674      */
2675     applyBody: function(obj)
2676     {
2677         if (!this.tmpl) {
2678             Roo.log("Error - using apply Body without a template");
2679             //code
2680         }
2681         this.tmpl.overwrite(this.bodyEl, obj);
2682     }
2683     
2684 });
2685
2686
2687 Roo.apply(Roo.bootstrap.Modal,  {
2688     /**
2689          * Button config that displays a single OK button
2690          * @type Object
2691          */
2692         OK :  [{
2693             name : 'ok',
2694             weight : 'primary',
2695             html : 'OK'
2696         }], 
2697         /**
2698          * Button config that displays Yes and No buttons
2699          * @type Object
2700          */
2701         YESNO : [
2702             {
2703                 name  : 'no',
2704                 html : 'No'
2705             },
2706             {
2707                 name  :'yes',
2708                 weight : 'primary',
2709                 html : 'Yes'
2710             }
2711         ],
2712         
2713         /**
2714          * Button config that displays OK and Cancel buttons
2715          * @type Object
2716          */
2717         OKCANCEL : [
2718             {
2719                name : 'cancel',
2720                 html : 'Cancel'
2721             },
2722             {
2723                 name : 'ok',
2724                 weight : 'primary',
2725                 html : 'OK'
2726             }
2727         ],
2728         /**
2729          * Button config that displays Yes, No and Cancel buttons
2730          * @type Object
2731          */
2732         YESNOCANCEL : [
2733             {
2734                 name : 'yes',
2735                 weight : 'primary',
2736                 html : 'Yes'
2737             },
2738             {
2739                 name : 'no',
2740                 html : 'No'
2741             },
2742             {
2743                 name : 'cancel',
2744                 html : 'Cancel'
2745             }
2746         ]
2747 });
2748  
2749  /*
2750  * - LGPL
2751  *
2752  * messagebox - can be used as a replace
2753  * 
2754  */
2755 /**
2756  * @class Roo.MessageBox
2757  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2758  * Example usage:
2759  *<pre><code>
2760 // Basic alert:
2761 Roo.Msg.alert('Status', 'Changes saved successfully.');
2762
2763 // Prompt for user data:
2764 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2765     if (btn == 'ok'){
2766         // process text value...
2767     }
2768 });
2769
2770 // Show a dialog using config options:
2771 Roo.Msg.show({
2772    title:'Save Changes?',
2773    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2774    buttons: Roo.Msg.YESNOCANCEL,
2775    fn: processResult,
2776    animEl: 'elId'
2777 });
2778 </code></pre>
2779  * @singleton
2780  */
2781 Roo.bootstrap.MessageBox = function(){
2782     var dlg, opt, mask, waitTimer;
2783     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2784     var buttons, activeTextEl, bwidth;
2785
2786     
2787     // private
2788     var handleButton = function(button){
2789         dlg.hide();
2790         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2791     };
2792
2793     // private
2794     var handleHide = function(){
2795         if(opt && opt.cls){
2796             dlg.el.removeClass(opt.cls);
2797         }
2798         //if(waitTimer){
2799         //    Roo.TaskMgr.stop(waitTimer);
2800         //    waitTimer = null;
2801         //}
2802     };
2803
2804     // private
2805     var updateButtons = function(b){
2806         var width = 0;
2807         if(!b){
2808             buttons["ok"].hide();
2809             buttons["cancel"].hide();
2810             buttons["yes"].hide();
2811             buttons["no"].hide();
2812             //dlg.footer.dom.style.display = 'none';
2813             return width;
2814         }
2815         dlg.footerEl.dom.style.display = '';
2816         for(var k in buttons){
2817             if(typeof buttons[k] != "function"){
2818                 if(b[k]){
2819                     buttons[k].show();
2820                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2821                     width += buttons[k].el.getWidth()+15;
2822                 }else{
2823                     buttons[k].hide();
2824                 }
2825             }
2826         }
2827         return width;
2828     };
2829
2830     // private
2831     var handleEsc = function(d, k, e){
2832         if(opt && opt.closable !== false){
2833             dlg.hide();
2834         }
2835         if(e){
2836             e.stopEvent();
2837         }
2838     };
2839
2840     return {
2841         /**
2842          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2843          * @return {Roo.BasicDialog} The BasicDialog element
2844          */
2845         getDialog : function(){
2846            if(!dlg){
2847                 dlg = new Roo.bootstrap.Modal( {
2848                     //draggable: true,
2849                     //resizable:false,
2850                     //constraintoviewport:false,
2851                     //fixedcenter:true,
2852                     //collapsible : false,
2853                     //shim:true,
2854                     //modal: true,
2855                   //  width:400,
2856                   //  height:100,
2857                     //buttonAlign:"center",
2858                     closeClick : function(){
2859                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2860                             handleButton("no");
2861                         }else{
2862                             handleButton("cancel");
2863                         }
2864                     }
2865                 });
2866                 dlg.render();
2867                 dlg.on("hide", handleHide);
2868                 mask = dlg.mask;
2869                 //dlg.addKeyListener(27, handleEsc);
2870                 buttons = {};
2871                 this.buttons = buttons;
2872                 var bt = this.buttonText;
2873                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2874                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2875                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2876                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2877                 Roo.log(buttons)
2878                 bodyEl = dlg.bodyEl.createChild({
2879
2880                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2881                         '<textarea class="roo-mb-textarea"></textarea>' +
2882                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2883                 });
2884                 msgEl = bodyEl.dom.firstChild;
2885                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2886                 textboxEl.enableDisplayMode();
2887                 textboxEl.addKeyListener([10,13], function(){
2888                     if(dlg.isVisible() && opt && opt.buttons){
2889                         if(opt.buttons.ok){
2890                             handleButton("ok");
2891                         }else if(opt.buttons.yes){
2892                             handleButton("yes");
2893                         }
2894                     }
2895                 });
2896                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2897                 textareaEl.enableDisplayMode();
2898                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2899                 progressEl.enableDisplayMode();
2900                 var pf = progressEl.dom.firstChild;
2901                 if (pf) {
2902                     pp = Roo.get(pf.firstChild);
2903                     pp.setHeight(pf.offsetHeight);
2904                 }
2905                 
2906             }
2907             return dlg;
2908         },
2909
2910         /**
2911          * Updates the message box body text
2912          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2913          * the XHTML-compliant non-breaking space character '&amp;#160;')
2914          * @return {Roo.MessageBox} This message box
2915          */
2916         updateText : function(text){
2917             if(!dlg.isVisible() && !opt.width){
2918                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2919             }
2920             msgEl.innerHTML = text || '&#160;';
2921       
2922             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2923             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2924             var w = Math.max(
2925                     Math.min(opt.width || cw , this.maxWidth), 
2926                     Math.max(opt.minWidth || this.minWidth, bwidth)
2927             );
2928             if(opt.prompt){
2929                 activeTextEl.setWidth(w);
2930             }
2931             if(dlg.isVisible()){
2932                 dlg.fixedcenter = false;
2933             }
2934             // to big, make it scroll. = But as usual stupid IE does not support
2935             // !important..
2936             
2937             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2938                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2939                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2940             } else {
2941                 bodyEl.dom.style.height = '';
2942                 bodyEl.dom.style.overflowY = '';
2943             }
2944             if (cw > w) {
2945                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2946             } else {
2947                 bodyEl.dom.style.overflowX = '';
2948             }
2949             
2950             dlg.setContentSize(w, bodyEl.getHeight());
2951             if(dlg.isVisible()){
2952                 dlg.fixedcenter = true;
2953             }
2954             return this;
2955         },
2956
2957         /**
2958          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2959          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2960          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2961          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2962          * @return {Roo.MessageBox} This message box
2963          */
2964         updateProgress : function(value, text){
2965             if(text){
2966                 this.updateText(text);
2967             }
2968             if (pp) { // weird bug on my firefox - for some reason this is not defined
2969                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2970             }
2971             return this;
2972         },        
2973
2974         /**
2975          * Returns true if the message box is currently displayed
2976          * @return {Boolean} True if the message box is visible, else false
2977          */
2978         isVisible : function(){
2979             return dlg && dlg.isVisible();  
2980         },
2981
2982         /**
2983          * Hides the message box if it is displayed
2984          */
2985         hide : function(){
2986             if(this.isVisible()){
2987                 dlg.hide();
2988             }  
2989         },
2990
2991         /**
2992          * Displays a new message box, or reinitializes an existing message box, based on the config options
2993          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2994          * The following config object properties are supported:
2995          * <pre>
2996 Property    Type             Description
2997 ----------  ---------------  ------------------------------------------------------------------------------------
2998 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2999                                    closes (defaults to undefined)
3000 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3001                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3002 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3003                                    progress and wait dialogs will ignore this property and always hide the
3004                                    close button as they can only be closed programmatically.
3005 cls               String           A custom CSS class to apply to the message box element
3006 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3007                                    displayed (defaults to 75)
3008 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3009                                    function will be btn (the name of the button that was clicked, if applicable,
3010                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3011                                    Progress and wait dialogs will ignore this option since they do not respond to
3012                                    user actions and can only be closed programmatically, so any required function
3013                                    should be called by the same code after it closes the dialog.
3014 icon              String           A CSS class that provides a background image to be used as an icon for
3015                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3016 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3017 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3018 modal             Boolean          False to allow user interaction with the page while the message box is
3019                                    displayed (defaults to true)
3020 msg               String           A string that will replace the existing message box body text (defaults
3021                                    to the XHTML-compliant non-breaking space character '&#160;')
3022 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3023 progress          Boolean          True to display a progress bar (defaults to false)
3024 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3025 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3026 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3027 title             String           The title text
3028 value             String           The string value to set into the active textbox element if displayed
3029 wait              Boolean          True to display a progress bar (defaults to false)
3030 width             Number           The width of the dialog in pixels
3031 </pre>
3032          *
3033          * Example usage:
3034          * <pre><code>
3035 Roo.Msg.show({
3036    title: 'Address',
3037    msg: 'Please enter your address:',
3038    width: 300,
3039    buttons: Roo.MessageBox.OKCANCEL,
3040    multiline: true,
3041    fn: saveAddress,
3042    animEl: 'addAddressBtn'
3043 });
3044 </code></pre>
3045          * @param {Object} config Configuration options
3046          * @return {Roo.MessageBox} This message box
3047          */
3048         show : function(options)
3049         {
3050             
3051             // this causes nightmares if you show one dialog after another
3052             // especially on callbacks..
3053              
3054             if(this.isVisible()){
3055                 
3056                 this.hide();
3057                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3058                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3059                 Roo.log("New Dialog Message:" +  options.msg )
3060                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3061                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3062                 
3063             }
3064             var d = this.getDialog();
3065             opt = options;
3066             d.setTitle(opt.title || "&#160;");
3067             d.closeEl.setDisplayed(opt.closable !== false);
3068             activeTextEl = textboxEl;
3069             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3070             if(opt.prompt){
3071                 if(opt.multiline){
3072                     textboxEl.hide();
3073                     textareaEl.show();
3074                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3075                         opt.multiline : this.defaultTextHeight);
3076                     activeTextEl = textareaEl;
3077                 }else{
3078                     textboxEl.show();
3079                     textareaEl.hide();
3080                 }
3081             }else{
3082                 textboxEl.hide();
3083                 textareaEl.hide();
3084             }
3085             progressEl.setDisplayed(opt.progress === true);
3086             this.updateProgress(0);
3087             activeTextEl.dom.value = opt.value || "";
3088             if(opt.prompt){
3089                 dlg.setDefaultButton(activeTextEl);
3090             }else{
3091                 var bs = opt.buttons;
3092                 var db = null;
3093                 if(bs && bs.ok){
3094                     db = buttons["ok"];
3095                 }else if(bs && bs.yes){
3096                     db = buttons["yes"];
3097                 }
3098                 dlg.setDefaultButton(db);
3099             }
3100             bwidth = updateButtons(opt.buttons);
3101             this.updateText(opt.msg);
3102             if(opt.cls){
3103                 d.el.addClass(opt.cls);
3104             }
3105             d.proxyDrag = opt.proxyDrag === true;
3106             d.modal = opt.modal !== false;
3107             d.mask = opt.modal !== false ? mask : false;
3108             if(!d.isVisible()){
3109                 // force it to the end of the z-index stack so it gets a cursor in FF
3110                 document.body.appendChild(dlg.el.dom);
3111                 d.animateTarget = null;
3112                 d.show(options.animEl);
3113             }
3114             return this;
3115         },
3116
3117         /**
3118          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3119          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3120          * and closing the message box when the process is complete.
3121          * @param {String} title The title bar text
3122          * @param {String} msg The message box body text
3123          * @return {Roo.MessageBox} This message box
3124          */
3125         progress : function(title, msg){
3126             this.show({
3127                 title : title,
3128                 msg : msg,
3129                 buttons: false,
3130                 progress:true,
3131                 closable:false,
3132                 minWidth: this.minProgressWidth,
3133                 modal : true
3134             });
3135             return this;
3136         },
3137
3138         /**
3139          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3140          * If a callback function is passed it will be called after the user clicks the button, and the
3141          * id of the button that was clicked will be passed as the only parameter to the callback
3142          * (could also be the top-right close button).
3143          * @param {String} title The title bar text
3144          * @param {String} msg The message box body text
3145          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3146          * @param {Object} scope (optional) The scope of the callback function
3147          * @return {Roo.MessageBox} This message box
3148          */
3149         alert : function(title, msg, fn, scope){
3150             this.show({
3151                 title : title,
3152                 msg : msg,
3153                 buttons: this.OK,
3154                 fn: fn,
3155                 scope : scope,
3156                 modal : true
3157             });
3158             return this;
3159         },
3160
3161         /**
3162          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3163          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3164          * You are responsible for closing the message box when the process is complete.
3165          * @param {String} msg The message box body text
3166          * @param {String} title (optional) The title bar text
3167          * @return {Roo.MessageBox} This message box
3168          */
3169         wait : function(msg, title){
3170             this.show({
3171                 title : title,
3172                 msg : msg,
3173                 buttons: false,
3174                 closable:false,
3175                 progress:true,
3176                 modal:true,
3177                 width:300,
3178                 wait:true
3179             });
3180             waitTimer = Roo.TaskMgr.start({
3181                 run: function(i){
3182                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3183                 },
3184                 interval: 1000
3185             });
3186             return this;
3187         },
3188
3189         /**
3190          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3191          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3192          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3193          * @param {String} title The title bar text
3194          * @param {String} msg The message box body text
3195          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3196          * @param {Object} scope (optional) The scope of the callback function
3197          * @return {Roo.MessageBox} This message box
3198          */
3199         confirm : function(title, msg, fn, scope){
3200             this.show({
3201                 title : title,
3202                 msg : msg,
3203                 buttons: this.YESNO,
3204                 fn: fn,
3205                 scope : scope,
3206                 modal : true
3207             });
3208             return this;
3209         },
3210
3211         /**
3212          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3213          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3214          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3215          * (could also be the top-right close button) and the text that was entered will be passed as the two
3216          * parameters to the callback.
3217          * @param {String} title The title bar text
3218          * @param {String} msg The message box body text
3219          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3220          * @param {Object} scope (optional) The scope of the callback function
3221          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3222          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3223          * @return {Roo.MessageBox} This message box
3224          */
3225         prompt : function(title, msg, fn, scope, multiline){
3226             this.show({
3227                 title : title,
3228                 msg : msg,
3229                 buttons: this.OKCANCEL,
3230                 fn: fn,
3231                 minWidth:250,
3232                 scope : scope,
3233                 prompt:true,
3234                 multiline: multiline,
3235                 modal : true
3236             });
3237             return this;
3238         },
3239
3240         /**
3241          * Button config that displays a single OK button
3242          * @type Object
3243          */
3244         OK : {ok:true},
3245         /**
3246          * Button config that displays Yes and No buttons
3247          * @type Object
3248          */
3249         YESNO : {yes:true, no:true},
3250         /**
3251          * Button config that displays OK and Cancel buttons
3252          * @type Object
3253          */
3254         OKCANCEL : {ok:true, cancel:true},
3255         /**
3256          * Button config that displays Yes, No and Cancel buttons
3257          * @type Object
3258          */
3259         YESNOCANCEL : {yes:true, no:true, cancel:true},
3260
3261         /**
3262          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3263          * @type Number
3264          */
3265         defaultTextHeight : 75,
3266         /**
3267          * The maximum width in pixels of the message box (defaults to 600)
3268          * @type Number
3269          */
3270         maxWidth : 600,
3271         /**
3272          * The minimum width in pixels of the message box (defaults to 100)
3273          * @type Number
3274          */
3275         minWidth : 100,
3276         /**
3277          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3278          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3279          * @type Number
3280          */
3281         minProgressWidth : 250,
3282         /**
3283          * An object containing the default button text strings that can be overriden for localized language support.
3284          * Supported properties are: ok, cancel, yes and no.
3285          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3286          * @type Object
3287          */
3288         buttonText : {
3289             ok : "OK",
3290             cancel : "Cancel",
3291             yes : "Yes",
3292             no : "No"
3293         }
3294     };
3295 }();
3296
3297 /**
3298  * Shorthand for {@link Roo.MessageBox}
3299  */
3300 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3301 Roo.Msg = Roo.Msg || Roo.MessageBox;
3302 /*
3303  * - LGPL
3304  *
3305  * navbar
3306  * 
3307  */
3308
3309 /**
3310  * @class Roo.bootstrap.Navbar
3311  * @extends Roo.bootstrap.Component
3312  * Bootstrap Navbar class
3313
3314  * @constructor
3315  * Create a new Navbar
3316  * @param {Object} config The config object
3317  */
3318
3319
3320 Roo.bootstrap.Navbar = function(config){
3321     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3322     
3323 };
3324
3325 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3326     
3327     
3328    
3329     // private
3330     navItems : false,
3331     loadMask : false,
3332     
3333     
3334     getAutoCreate : function(){
3335         
3336         
3337         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3338         
3339     },
3340     
3341     initEvents :function ()
3342     {
3343         //Roo.log(this.el.select('.navbar-toggle',true));
3344         this.el.select('.navbar-toggle',true).on('click', function() {
3345            // Roo.log('click');
3346             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3347         }, this);
3348         
3349         var mark = {
3350             tag: "div",
3351             cls:"x-dlg-mask"
3352         };
3353         
3354         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3355         
3356         var size = this.el.getSize();
3357         this.maskEl.setSize(size.width, size.height);
3358         this.maskEl.enableDisplayMode("block");
3359         this.maskEl.hide();
3360         
3361         if(this.loadMask){
3362             this.maskEl.show();
3363         }
3364     },
3365     
3366     
3367     getChildContainer : function()
3368     {
3369         if (this.el.select('.collapse').getCount()) {
3370             return this.el.select('.collapse',true).first();
3371         }
3372         
3373         return this.el;
3374     },
3375     
3376     mask : function()
3377     {
3378         this.maskEl.show();
3379     },
3380     
3381     unmask : function()
3382     {
3383         this.maskEl.hide();
3384     } 
3385     
3386     
3387     
3388     
3389 });
3390
3391
3392
3393  
3394
3395  /*
3396  * - LGPL
3397  *
3398  * navbar
3399  * 
3400  */
3401
3402 /**
3403  * @class Roo.bootstrap.NavSimplebar
3404  * @extends Roo.bootstrap.Navbar
3405  * Bootstrap Sidebar class
3406  *
3407  * @cfg {Boolean} inverse is inverted color
3408  * 
3409  * @cfg {String} type (nav | pills | tabs)
3410  * @cfg {Boolean} arrangement stacked | justified
3411  * @cfg {String} align (left | right) alignment
3412  * 
3413  * @cfg {Boolean} main (true|false) main nav bar? default false
3414  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3415  * 
3416  * @cfg {String} tag (header|footer|nav|div) default is nav 
3417
3418  * 
3419  * 
3420  * 
3421  * @constructor
3422  * Create a new Sidebar
3423  * @param {Object} config The config object
3424  */
3425
3426
3427 Roo.bootstrap.NavSimplebar = function(config){
3428     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3429 };
3430
3431 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3432     
3433     inverse: false,
3434     
3435     type: false,
3436     arrangement: '',
3437     align : false,
3438     
3439     
3440     
3441     main : false,
3442     
3443     
3444     tag : false,
3445     
3446     
3447     getAutoCreate : function(){
3448         
3449         
3450         var cfg = {
3451             tag : this.tag || 'div',
3452             cls : 'navbar'
3453         };
3454           
3455         
3456         cfg.cn = [
3457             {
3458                 cls: 'nav',
3459                 tag : 'ul'
3460             }
3461         ];
3462         
3463          
3464         this.type = this.type || 'nav';
3465         if (['tabs','pills'].indexOf(this.type)!==-1) {
3466             cfg.cn[0].cls += ' nav-' + this.type
3467         
3468         
3469         } else {
3470             if (this.type!=='nav') {
3471                 Roo.log('nav type must be nav/tabs/pills')
3472             }
3473             cfg.cn[0].cls += ' navbar-nav'
3474         }
3475         
3476         
3477         
3478         
3479         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3480             cfg.cn[0].cls += ' nav-' + this.arrangement;
3481         }
3482         
3483         
3484         if (this.align === 'right') {
3485             cfg.cn[0].cls += ' navbar-right';
3486         }
3487         
3488         if (this.inverse) {
3489             cfg.cls += ' navbar-inverse';
3490             
3491         }
3492         
3493         
3494         return cfg;
3495     
3496         
3497     }
3498     
3499     
3500     
3501 });
3502
3503
3504
3505  
3506
3507  
3508        /*
3509  * - LGPL
3510  *
3511  * navbar
3512  * 
3513  */
3514
3515 /**
3516  * @class Roo.bootstrap.NavHeaderbar
3517  * @extends Roo.bootstrap.NavSimplebar
3518  * Bootstrap Sidebar class
3519  *
3520  * @cfg {String} brand what is brand
3521  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3522  * @cfg {String} brand_href href of the brand
3523  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3524  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3525  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3526  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3527  * 
3528  * @constructor
3529  * Create a new Sidebar
3530  * @param {Object} config The config object
3531  */
3532
3533
3534 Roo.bootstrap.NavHeaderbar = function(config){
3535     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3536       
3537 };
3538
3539 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3540     
3541     position: '',
3542     brand: '',
3543     brand_href: false,
3544     srButton : true,
3545     autohide : false,
3546     desktopCenter : false,
3547    
3548     
3549     getAutoCreate : function(){
3550         
3551         var   cfg = {
3552             tag: this.nav || 'nav',
3553             cls: 'navbar',
3554             role: 'navigation',
3555             cn: []
3556         };
3557         
3558         var cn = cfg.cn;
3559         if (this.desktopCenter) {
3560             cn.push({cls : 'container', cn : []});
3561             cn = cn[0].cn;
3562         }
3563         
3564         if(this.srButton){
3565             cn.push({
3566                 tag: 'div',
3567                 cls: 'navbar-header',
3568                 cn: [
3569                     {
3570                         tag: 'button',
3571                         type: 'button',
3572                         cls: 'navbar-toggle',
3573                         'data-toggle': 'collapse',
3574                         cn: [
3575                             {
3576                                 tag: 'span',
3577                                 cls: 'sr-only',
3578                                 html: 'Toggle navigation'
3579                             },
3580                             {
3581                                 tag: 'span',
3582                                 cls: 'icon-bar'
3583                             },
3584                             {
3585                                 tag: 'span',
3586                                 cls: 'icon-bar'
3587                             },
3588                             {
3589                                 tag: 'span',
3590                                 cls: 'icon-bar'
3591                             }
3592                         ]
3593                     }
3594                 ]
3595             });
3596         }
3597         
3598         cn.push({
3599             tag: 'div',
3600             cls: 'collapse navbar-collapse',
3601             cn : []
3602         });
3603         
3604         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3605         
3606         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3607             cfg.cls += ' navbar-' + this.position;
3608             
3609             // tag can override this..
3610             
3611             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3612         }
3613         
3614         if (this.brand !== '') {
3615             cn[0].cn.push({
3616                 tag: 'a',
3617                 href: this.brand_href ? this.brand_href : '#',
3618                 cls: 'navbar-brand',
3619                 cn: [
3620                 this.brand
3621                 ]
3622             });
3623         }
3624         
3625         if(this.main){
3626             cfg.cls += ' main-nav';
3627         }
3628         
3629         
3630         return cfg;
3631
3632         
3633     },
3634     getHeaderChildContainer : function()
3635     {
3636         if (this.el.select('.navbar-header').getCount()) {
3637             return this.el.select('.navbar-header',true).first();
3638         }
3639         
3640         return this.getChildContainer();
3641     },
3642     
3643     
3644     initEvents : function()
3645     {
3646         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3647         
3648         if (this.autohide) {
3649             
3650             var prevScroll = 0;
3651             var ft = this.el;
3652             
3653             Roo.get(document).on('scroll',function(e) {
3654                 var ns = Roo.get(document).getScroll().top;
3655                 var os = prevScroll;
3656                 prevScroll = ns;
3657                 
3658                 if(ns > os){
3659                     ft.removeClass('slideDown');
3660                     ft.addClass('slideUp');
3661                     return;
3662                 }
3663                 ft.removeClass('slideUp');
3664                 ft.addClass('slideDown');
3665                  
3666               
3667           },this);
3668         }
3669     }    
3670     
3671 });
3672
3673
3674
3675  
3676
3677  /*
3678  * - LGPL
3679  *
3680  * navbar
3681  * 
3682  */
3683
3684 /**
3685  * @class Roo.bootstrap.NavSidebar
3686  * @extends Roo.bootstrap.Navbar
3687  * Bootstrap Sidebar class
3688  * 
3689  * @constructor
3690  * Create a new Sidebar
3691  * @param {Object} config The config object
3692  */
3693
3694
3695 Roo.bootstrap.NavSidebar = function(config){
3696     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3697 };
3698
3699 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3700     
3701     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3702     
3703     getAutoCreate : function(){
3704         
3705         
3706         return  {
3707             tag: 'div',
3708             cls: 'sidebar sidebar-nav'
3709         };
3710     
3711         
3712     }
3713     
3714     
3715     
3716 });
3717
3718
3719
3720  
3721
3722  /*
3723  * - LGPL
3724  *
3725  * nav group
3726  * 
3727  */
3728
3729 /**
3730  * @class Roo.bootstrap.NavGroup
3731  * @extends Roo.bootstrap.Component
3732  * Bootstrap NavGroup class
3733  * @cfg {String} align (left|right)
3734  * @cfg {Boolean} inverse
3735  * @cfg {String} type (nav|pills|tab) default nav
3736  * @cfg {String} navId - reference Id for navbar.
3737
3738  * 
3739  * @constructor
3740  * Create a new nav group
3741  * @param {Object} config The config object
3742  */
3743
3744 Roo.bootstrap.NavGroup = function(config){
3745     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3746     this.navItems = [];
3747    
3748     Roo.bootstrap.NavGroup.register(this);
3749      this.addEvents({
3750         /**
3751              * @event changed
3752              * Fires when the active item changes
3753              * @param {Roo.bootstrap.NavGroup} this
3754              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3755              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3756          */
3757         'changed': true
3758      });
3759     
3760 };
3761
3762 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3763     
3764     align: '',
3765     inverse: false,
3766     form: false,
3767     type: 'nav',
3768     navId : '',
3769     // private
3770     
3771     navItems : false, 
3772     
3773     getAutoCreate : function()
3774     {
3775         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3776         
3777         cfg = {
3778             tag : 'ul',
3779             cls: 'nav' 
3780         };
3781         
3782         if (['tabs','pills'].indexOf(this.type)!==-1) {
3783             cfg.cls += ' nav-' + this.type
3784         } else {
3785             if (this.type!=='nav') {
3786                 Roo.log('nav type must be nav/tabs/pills')
3787             }
3788             cfg.cls += ' navbar-nav'
3789         }
3790         
3791         if (this.parent().sidebar) {
3792             cfg = {
3793                 tag: 'ul',
3794                 cls: 'dashboard-menu sidebar-menu'
3795             };
3796             
3797             return cfg;
3798         }
3799         
3800         if (this.form === true) {
3801             cfg = {
3802                 tag: 'form',
3803                 cls: 'navbar-form'
3804             };
3805             
3806             if (this.align === 'right') {
3807                 cfg.cls += ' navbar-right';
3808             } else {
3809                 cfg.cls += ' navbar-left';
3810             }
3811         }
3812         
3813         if (this.align === 'right') {
3814             cfg.cls += ' navbar-right';
3815         }
3816         
3817         if (this.inverse) {
3818             cfg.cls += ' navbar-inverse';
3819             
3820         }
3821         
3822         
3823         return cfg;
3824     },
3825     /**
3826     * sets the active Navigation item
3827     * @param {Roo.bootstrap.NavItem} the new current navitem
3828     */
3829     setActiveItem : function(item)
3830     {
3831         var prev = false;
3832         Roo.each(this.navItems, function(v){
3833             if (v == item) {
3834                 return ;
3835             }
3836             if (v.isActive()) {
3837                 v.setActive(false, true);
3838                 prev = v;
3839                 
3840             }
3841             
3842         });
3843
3844         item.setActive(true, true);
3845         this.fireEvent('changed', this, item, prev);
3846         
3847         
3848     },
3849     /**
3850     * gets the active Navigation item
3851     * @return {Roo.bootstrap.NavItem} the current navitem
3852     */
3853     getActive : function()
3854     {
3855         
3856         var prev = false;
3857         Roo.each(this.navItems, function(v){
3858             
3859             if (v.isActive()) {
3860                 prev = v;
3861                 
3862             }
3863             
3864         });
3865         return prev;
3866     },
3867     
3868     indexOfNav : function()
3869     {
3870         
3871         var prev = false;
3872         Roo.each(this.navItems, function(v,i){
3873             
3874             if (v.isActive()) {
3875                 prev = i;
3876                 
3877             }
3878             
3879         });
3880         return prev;
3881     },
3882     /**
3883     * adds a Navigation item
3884     * @param {Roo.bootstrap.NavItem} the navitem to add
3885     */
3886     addItem : function(cfg)
3887     {
3888         var cn = new Roo.bootstrap.NavItem(cfg);
3889         this.register(cn);
3890         cn.parentId = this.id;
3891         cn.onRender(this.el, null);
3892         return cn;
3893     },
3894     /**
3895     * register a Navigation item
3896     * @param {Roo.bootstrap.NavItem} the navitem to add
3897     */
3898     register : function(item)
3899     {
3900         this.navItems.push( item);
3901         item.navId = this.navId;
3902     
3903     },
3904     
3905     /**
3906     * clear all the Navigation item
3907     */
3908    
3909     clearAll : function()
3910     {
3911         this.navItems = [];
3912         this.el.dom.innerHTML = '';
3913     },
3914     
3915     getNavItem: function(tabId)
3916     {
3917         var ret = false;
3918         Roo.each(this.navItems, function(e) {
3919             if (e.tabId == tabId) {
3920                ret =  e;
3921                return false;
3922             }
3923             return true;
3924             
3925         });
3926         return ret;
3927     },
3928     
3929     setActiveNext : function()
3930     {
3931         var i = this.indexOfNav(this.getActive());
3932         if (i > this.navItems.length) {
3933             return;
3934         }
3935         this.setActiveItem(this.navItems[i+1]);
3936     },
3937     setActivePrev : function()
3938     {
3939         var i = this.indexOfNav(this.getActive());
3940         if (i  < 1) {
3941             return;
3942         }
3943         this.setActiveItem(this.navItems[i-1]);
3944     },
3945     clearWasActive : function(except) {
3946         Roo.each(this.navItems, function(e) {
3947             if (e.tabId != except.tabId && e.was_active) {
3948                e.was_active = false;
3949                return false;
3950             }
3951             return true;
3952             
3953         });
3954     },
3955     getWasActive : function ()
3956     {
3957         var r = false;
3958         Roo.each(this.navItems, function(e) {
3959             if (e.was_active) {
3960                r = e;
3961                return false;
3962             }
3963             return true;
3964             
3965         });
3966         return r;
3967     }
3968     
3969     
3970 });
3971
3972  
3973 Roo.apply(Roo.bootstrap.NavGroup, {
3974     
3975     groups: {},
3976      /**
3977     * register a Navigation Group
3978     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3979     */
3980     register : function(navgrp)
3981     {
3982         this.groups[navgrp.navId] = navgrp;
3983         
3984     },
3985     /**
3986     * fetch a Navigation Group based on the navigation ID
3987     * @param {string} the navgroup to add
3988     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3989     */
3990     get: function(navId) {
3991         if (typeof(this.groups[navId]) == 'undefined') {
3992             return false;
3993             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3994         }
3995         return this.groups[navId] ;
3996     }
3997     
3998     
3999     
4000 });
4001
4002  /*
4003  * - LGPL
4004  *
4005  * row
4006  * 
4007  */
4008
4009 /**
4010  * @class Roo.bootstrap.NavItem
4011  * @extends Roo.bootstrap.Component
4012  * Bootstrap Navbar.NavItem class
4013  * @cfg {String} href  link to
4014  * @cfg {String} html content of button
4015  * @cfg {String} badge text inside badge
4016  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4017  * @cfg {String} glyphicon name of glyphicon
4018  * @cfg {String} icon name of font awesome icon
4019  * @cfg {Boolean} active Is item active
4020  * @cfg {Boolean} disabled Is item disabled
4021  
4022  * @cfg {Boolean} preventDefault (true | false) default false
4023  * @cfg {String} tabId the tab that this item activates.
4024  * @cfg {String} tagtype (a|span) render as a href or span?
4025  * @cfg {Boolean} animateRef (true|false) link to element default false  
4026   
4027  * @constructor
4028  * Create a new Navbar Item
4029  * @param {Object} config The config object
4030  */
4031 Roo.bootstrap.NavItem = function(config){
4032     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4033     this.addEvents({
4034         // raw events
4035         /**
4036          * @event click
4037          * The raw click event for the entire grid.
4038          * @param {Roo.EventObject} e
4039          */
4040         "click" : true,
4041          /**
4042             * @event changed
4043             * Fires when the active item active state changes
4044             * @param {Roo.bootstrap.NavItem} this
4045             * @param {boolean} state the new state
4046              
4047          */
4048         'changed': true,
4049         /**
4050             * @event scrollto
4051             * Fires when scroll to element
4052             * @param {Roo.bootstrap.NavItem} this
4053             * @param {Object} options
4054             * @param {Roo.EventObject} e
4055              
4056          */
4057         'scrollto': true
4058     });
4059    
4060 };
4061
4062 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4063     
4064     href: false,
4065     html: '',
4066     badge: '',
4067     icon: false,
4068     glyphicon: false,
4069     active: false,
4070     preventDefault : false,
4071     tabId : false,
4072     tagtype : 'a',
4073     disabled : false,
4074     animateRef : false,
4075     was_active : false,
4076     
4077     getAutoCreate : function(){
4078          
4079         var cfg = {
4080             tag: 'li',
4081             cls: 'nav-item'
4082             
4083         };
4084         
4085         if (this.active) {
4086             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4087         }
4088         if (this.disabled) {
4089             cfg.cls += ' disabled';
4090         }
4091         
4092         if (this.href || this.html || this.glyphicon || this.icon) {
4093             cfg.cn = [
4094                 {
4095                     tag: this.tagtype,
4096                     href : this.href || "#",
4097                     html: this.html || ''
4098                 }
4099             ];
4100             
4101             if (this.icon) {
4102                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4103             }
4104
4105             if(this.glyphicon) {
4106                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4107             }
4108             
4109             if (this.menu) {
4110                 
4111                 cfg.cn[0].html += " <span class='caret'></span>";
4112              
4113             }
4114             
4115             if (this.badge !== '') {
4116                  
4117                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4118             }
4119         }
4120         
4121         
4122         
4123         return cfg;
4124     },
4125     initEvents: function() 
4126     {
4127         if (typeof (this.menu) != 'undefined') {
4128             this.menu.parentType = this.xtype;
4129             this.menu.triggerEl = this.el;
4130             this.menu = this.addxtype(Roo.apply({}, this.menu));
4131         }
4132         
4133         this.el.select('a',true).on('click', this.onClick, this);
4134         
4135         if(this.tagtype == 'span'){
4136             this.el.select('span',true).on('click', this.onClick, this);
4137         }
4138        
4139         // at this point parent should be available..
4140         this.parent().register(this);
4141     },
4142     
4143     onClick : function(e)
4144     {
4145         if(
4146                 this.preventDefault || 
4147                 this.href == '#' 
4148         ){
4149             
4150             e.preventDefault();
4151         }
4152         
4153         if (this.disabled) {
4154             return;
4155         }
4156         
4157         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4158         if (tg && tg.transition) {
4159             Roo.log("waiting for the transitionend");
4160             return;
4161         }
4162         
4163         
4164         
4165         //Roo.log("fire event clicked");
4166         if(this.fireEvent('click', this, e) === false){
4167             return;
4168         };
4169         
4170         if(this.tagtype == 'span'){
4171             return;
4172         }
4173         
4174         //Roo.log(this.href);
4175         var ael = this.el.select('a',true).first();
4176         //Roo.log(ael);
4177         
4178         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4179             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4180             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4181                 return; // ignore... - it's a 'hash' to another page.
4182             }
4183             
4184             e.preventDefault();
4185             this.scrollToElement(e);
4186         }
4187         
4188         
4189         var p =  this.parent();
4190    
4191         if (['tabs','pills'].indexOf(p.type)!==-1) {
4192             if (typeof(p.setActiveItem) !== 'undefined') {
4193                 p.setActiveItem(this);
4194             }
4195         }
4196         
4197         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4198         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4199             // remove the collapsed menu expand...
4200             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4201         }
4202     },
4203     
4204     isActive: function () {
4205         return this.active
4206     },
4207     setActive : function(state, fire, is_was_active)
4208     {
4209         if (this.active && !state && this.navId) {
4210             this.was_active = true;
4211             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4212             if (nv) {
4213                 nv.clearWasActive(this);
4214             }
4215             
4216         }
4217         this.active = state;
4218         
4219         if (!state ) {
4220             this.el.removeClass('active');
4221         } else if (!this.el.hasClass('active')) {
4222             this.el.addClass('active');
4223         }
4224         if (fire) {
4225             this.fireEvent('changed', this, state);
4226         }
4227         
4228         // show a panel if it's registered and related..
4229         
4230         if (!this.navId || !this.tabId || !state || is_was_active) {
4231             return;
4232         }
4233         
4234         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4235         if (!tg) {
4236             return;
4237         }
4238         var pan = tg.getPanelByName(this.tabId);
4239         if (!pan) {
4240             return;
4241         }
4242         // if we can not flip to new panel - go back to old nav highlight..
4243         if (false == tg.showPanel(pan)) {
4244             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4245             if (nv) {
4246                 var onav = nv.getWasActive();
4247                 if (onav) {
4248                     onav.setActive(true, false, true);
4249                 }
4250             }
4251             
4252         }
4253         
4254         
4255         
4256     },
4257      // this should not be here...
4258     setDisabled : function(state)
4259     {
4260         this.disabled = state;
4261         if (!state ) {
4262             this.el.removeClass('disabled');
4263         } else if (!this.el.hasClass('disabled')) {
4264             this.el.addClass('disabled');
4265         }
4266         
4267     },
4268     
4269     /**
4270      * Fetch the element to display the tooltip on.
4271      * @return {Roo.Element} defaults to this.el
4272      */
4273     tooltipEl : function()
4274     {
4275         return this.el.select('' + this.tagtype + '', true).first();
4276     },
4277     
4278     scrollToElement : function(e)
4279     {
4280         var c = document.body;
4281         
4282         /*
4283          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4284          */
4285         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4286             c = document.documentElement;
4287         }
4288         
4289         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4290         
4291         if(!target){
4292             return;
4293         }
4294
4295         var o = target.calcOffsetsTo(c);
4296         
4297         var options = {
4298             target : target,
4299             value : o[1]
4300         };
4301         
4302         this.fireEvent('scrollto', this, options, e);
4303         
4304         Roo.get(c).scrollTo('top', options.value, true);
4305         
4306         return;
4307     }
4308 });
4309  
4310
4311  /*
4312  * - LGPL
4313  *
4314  * sidebar item
4315  *
4316  *  li
4317  *    <span> icon </span>
4318  *    <span> text </span>
4319  *    <span>badge </span>
4320  */
4321
4322 /**
4323  * @class Roo.bootstrap.NavSidebarItem
4324  * @extends Roo.bootstrap.NavItem
4325  * Bootstrap Navbar.NavSidebarItem class
4326  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4327  * @constructor
4328  * Create a new Navbar Button
4329  * @param {Object} config The config object
4330  */
4331 Roo.bootstrap.NavSidebarItem = function(config){
4332     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4333     this.addEvents({
4334         // raw events
4335         /**
4336          * @event click
4337          * The raw click event for the entire grid.
4338          * @param {Roo.EventObject} e
4339          */
4340         "click" : true,
4341          /**
4342             * @event changed
4343             * Fires when the active item active state changes
4344             * @param {Roo.bootstrap.NavSidebarItem} this
4345             * @param {boolean} state the new state
4346              
4347          */
4348         'changed': true
4349     });
4350    
4351 };
4352
4353 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4354     
4355     badgeWeight : 'default',
4356     
4357     getAutoCreate : function(){
4358         
4359         
4360         var a = {
4361                 tag: 'a',
4362                 href : this.href || '#',
4363                 cls: '',
4364                 html : '',
4365                 cn : []
4366         };
4367         var cfg = {
4368             tag: 'li',
4369             cls: '',
4370             cn: [ a ]
4371         };
4372         var span = {
4373             tag: 'span',
4374             html : this.html || ''
4375         };
4376         
4377         
4378         if (this.active) {
4379             cfg.cls += ' active';
4380         }
4381         
4382         if (this.disabled) {
4383             cfg.cls += ' disabled';
4384         }
4385         
4386         // left icon..
4387         if (this.glyphicon || this.icon) {
4388             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4389             a.cn.push({ tag : 'i', cls : c }) ;
4390         }
4391         // html..
4392         a.cn.push(span);
4393         // then badge..
4394         if (this.badge !== '') {
4395             
4396             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4397         }
4398         // fi
4399         if (this.menu) {
4400             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4401             a.cls += 'dropdown-toggle treeview' ;
4402             
4403         }
4404         
4405         
4406         
4407         return cfg;
4408          
4409            
4410     },
4411     
4412     initEvents : function()
4413     { 
4414         this.el.on('click', this.onClick, this);
4415        
4416     
4417         if(this.badge !== ''){
4418  
4419             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4420         }
4421         
4422     },
4423     
4424     onClick : function(e)
4425     {
4426         if(this.disabled){
4427             e.preventDefault();
4428             return;
4429         }
4430         
4431         if(this.preventDefault){
4432             e.preventDefault();
4433         }
4434         
4435         this.fireEvent('click', this);
4436     },
4437     
4438     disable : function()
4439     {
4440         this.setDisabled(true);
4441     },
4442     
4443     enable : function()
4444     {
4445         this.setDisabled(false);
4446     },
4447     
4448     setDisabled : function(state)
4449     {
4450         if(this.disabled == state){
4451             return;
4452         }
4453         
4454         this.disabled = state;
4455         
4456         if (state) {
4457             this.el.addClass('disabled');
4458             return;
4459         }
4460         
4461         this.el.removeClass('disabled');
4462         
4463         return;
4464     },
4465     
4466     setActive : function(state)
4467     {
4468         if(this.active == state){
4469             return;
4470         }
4471         
4472         this.active = state;
4473         
4474         if (state) {
4475             this.el.addClass('active');
4476             return;
4477         }
4478         
4479         this.el.removeClass('active');
4480         
4481         return;
4482     },
4483     
4484     isActive: function () 
4485     {
4486         return this.active;
4487     },
4488     
4489     setBadge : function(str)
4490     {
4491         if(!this.badgeEl){
4492             return;
4493         }
4494         
4495         this.badgeEl.dom.innerHTML = str;
4496     }
4497     
4498    
4499      
4500  
4501 });
4502  
4503
4504  /*
4505  * - LGPL
4506  *
4507  * row
4508  * 
4509  */
4510
4511 /**
4512  * @class Roo.bootstrap.Row
4513  * @extends Roo.bootstrap.Component
4514  * Bootstrap Row class (contains columns...)
4515  * 
4516  * @constructor
4517  * Create a new Row
4518  * @param {Object} config The config object
4519  */
4520
4521 Roo.bootstrap.Row = function(config){
4522     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4523 };
4524
4525 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4526     
4527     getAutoCreate : function(){
4528        return {
4529             cls: 'row clearfix'
4530        };
4531     }
4532     
4533     
4534 });
4535
4536  
4537
4538  /*
4539  * - LGPL
4540  *
4541  * element
4542  * 
4543  */
4544
4545 /**
4546  * @class Roo.bootstrap.Element
4547  * @extends Roo.bootstrap.Component
4548  * Bootstrap Element class
4549  * @cfg {String} html contents of the element
4550  * @cfg {String} tag tag of the element
4551  * @cfg {String} cls class of the element
4552  * @cfg {Boolean} preventDefault (true|false) default false
4553  * @cfg {Boolean} clickable (true|false) default false
4554  * 
4555  * @constructor
4556  * Create a new Element
4557  * @param {Object} config The config object
4558  */
4559
4560 Roo.bootstrap.Element = function(config){
4561     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4562     
4563     this.addEvents({
4564         // raw events
4565         /**
4566          * @event click
4567          * When a element is chick
4568          * @param {Roo.bootstrap.Element} this
4569          * @param {Roo.EventObject} e
4570          */
4571         "click" : true
4572     });
4573 };
4574
4575 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4576     
4577     tag: 'div',
4578     cls: '',
4579     html: '',
4580     preventDefault: false, 
4581     clickable: false,
4582     
4583     getAutoCreate : function(){
4584         
4585         var cfg = {
4586             tag: this.tag,
4587             cls: this.cls,
4588             html: this.html
4589         };
4590         
4591         return cfg;
4592     },
4593     
4594     initEvents: function() 
4595     {
4596         Roo.bootstrap.Element.superclass.initEvents.call(this);
4597         
4598         if(this.clickable){
4599             this.el.on('click', this.onClick, this);
4600         }
4601         
4602     },
4603     
4604     onClick : function(e)
4605     {
4606         if(this.preventDefault){
4607             e.preventDefault();
4608         }
4609         
4610         this.fireEvent('click', this, e);
4611     },
4612     
4613     getValue : function()
4614     {
4615         return this.el.dom.innerHTML;
4616     },
4617     
4618     setValue : function(value)
4619     {
4620         this.el.dom.innerHTML = value;
4621     }
4622    
4623 });
4624
4625  
4626
4627  /*
4628  * - LGPL
4629  *
4630  * pagination
4631  * 
4632  */
4633
4634 /**
4635  * @class Roo.bootstrap.Pagination
4636  * @extends Roo.bootstrap.Component
4637  * Bootstrap Pagination class
4638  * @cfg {String} size xs | sm | md | lg
4639  * @cfg {Boolean} inverse false | true
4640  * 
4641  * @constructor
4642  * Create a new Pagination
4643  * @param {Object} config The config object
4644  */
4645
4646 Roo.bootstrap.Pagination = function(config){
4647     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4648 };
4649
4650 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4651     
4652     cls: false,
4653     size: false,
4654     inverse: false,
4655     
4656     getAutoCreate : function(){
4657         var cfg = {
4658             tag: 'ul',
4659                 cls: 'pagination'
4660         };
4661         if (this.inverse) {
4662             cfg.cls += ' inverse';
4663         }
4664         if (this.html) {
4665             cfg.html=this.html;
4666         }
4667         if (this.cls) {
4668             cfg.cls += " " + this.cls;
4669         }
4670         return cfg;
4671     }
4672    
4673 });
4674
4675  
4676
4677  /*
4678  * - LGPL
4679  *
4680  * Pagination item
4681  * 
4682  */
4683
4684
4685 /**
4686  * @class Roo.bootstrap.PaginationItem
4687  * @extends Roo.bootstrap.Component
4688  * Bootstrap PaginationItem class
4689  * @cfg {String} html text
4690  * @cfg {String} href the link
4691  * @cfg {Boolean} preventDefault (true | false) default true
4692  * @cfg {Boolean} active (true | false) default false
4693  * @cfg {Boolean} disabled default false
4694  * 
4695  * 
4696  * @constructor
4697  * Create a new PaginationItem
4698  * @param {Object} config The config object
4699  */
4700
4701
4702 Roo.bootstrap.PaginationItem = function(config){
4703     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4704     this.addEvents({
4705         // raw events
4706         /**
4707          * @event click
4708          * The raw click event for the entire grid.
4709          * @param {Roo.EventObject} e
4710          */
4711         "click" : true
4712     });
4713 };
4714
4715 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4716     
4717     href : false,
4718     html : false,
4719     preventDefault: true,
4720     active : false,
4721     cls : false,
4722     disabled: false,
4723     
4724     getAutoCreate : function(){
4725         var cfg= {
4726             tag: 'li',
4727             cn: [
4728                 {
4729                     tag : 'a',
4730                     href : this.href ? this.href : '#',
4731                     html : this.html ? this.html : ''
4732                 }
4733             ]
4734         };
4735         
4736         if(this.cls){
4737             cfg.cls = this.cls;
4738         }
4739         
4740         if(this.disabled){
4741             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4742         }
4743         
4744         if(this.active){
4745             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4746         }
4747         
4748         return cfg;
4749     },
4750     
4751     initEvents: function() {
4752         
4753         this.el.on('click', this.onClick, this);
4754         
4755     },
4756     onClick : function(e)
4757     {
4758         Roo.log('PaginationItem on click ');
4759         if(this.preventDefault){
4760             e.preventDefault();
4761         }
4762         
4763         if(this.disabled){
4764             return;
4765         }
4766         
4767         this.fireEvent('click', this, e);
4768     }
4769    
4770 });
4771
4772  
4773
4774  /*
4775  * - LGPL
4776  *
4777  * slider
4778  * 
4779  */
4780
4781
4782 /**
4783  * @class Roo.bootstrap.Slider
4784  * @extends Roo.bootstrap.Component
4785  * Bootstrap Slider class
4786  *    
4787  * @constructor
4788  * Create a new Slider
4789  * @param {Object} config The config object
4790  */
4791
4792 Roo.bootstrap.Slider = function(config){
4793     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4794 };
4795
4796 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4797     
4798     getAutoCreate : function(){
4799         
4800         var cfg = {
4801             tag: 'div',
4802             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4803             cn: [
4804                 {
4805                     tag: 'a',
4806                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4807                 }
4808             ]
4809         };
4810         
4811         return cfg;
4812     }
4813    
4814 });
4815
4816  /*
4817  * Based on:
4818  * Ext JS Library 1.1.1
4819  * Copyright(c) 2006-2007, Ext JS, LLC.
4820  *
4821  * Originally Released Under LGPL - original licence link has changed is not relivant.
4822  *
4823  * Fork - LGPL
4824  * <script type="text/javascript">
4825  */
4826  
4827
4828 /**
4829  * @class Roo.grid.ColumnModel
4830  * @extends Roo.util.Observable
4831  * This is the default implementation of a ColumnModel used by the Grid. It defines
4832  * the columns in the grid.
4833  * <br>Usage:<br>
4834  <pre><code>
4835  var colModel = new Roo.grid.ColumnModel([
4836         {header: "Ticker", width: 60, sortable: true, locked: true},
4837         {header: "Company Name", width: 150, sortable: true},
4838         {header: "Market Cap.", width: 100, sortable: true},
4839         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4840         {header: "Employees", width: 100, sortable: true, resizable: false}
4841  ]);
4842  </code></pre>
4843  * <p>
4844  
4845  * The config options listed for this class are options which may appear in each
4846  * individual column definition.
4847  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4848  * @constructor
4849  * @param {Object} config An Array of column config objects. See this class's
4850  * config objects for details.
4851 */
4852 Roo.grid.ColumnModel = function(config){
4853         /**
4854      * The config passed into the constructor
4855      */
4856     this.config = config;
4857     this.lookup = {};
4858
4859     // if no id, create one
4860     // if the column does not have a dataIndex mapping,
4861     // map it to the order it is in the config
4862     for(var i = 0, len = config.length; i < len; i++){
4863         var c = config[i];
4864         if(typeof c.dataIndex == "undefined"){
4865             c.dataIndex = i;
4866         }
4867         if(typeof c.renderer == "string"){
4868             c.renderer = Roo.util.Format[c.renderer];
4869         }
4870         if(typeof c.id == "undefined"){
4871             c.id = Roo.id();
4872         }
4873         if(c.editor && c.editor.xtype){
4874             c.editor  = Roo.factory(c.editor, Roo.grid);
4875         }
4876         if(c.editor && c.editor.isFormField){
4877             c.editor = new Roo.grid.GridEditor(c.editor);
4878         }
4879         this.lookup[c.id] = c;
4880     }
4881
4882     /**
4883      * The width of columns which have no width specified (defaults to 100)
4884      * @type Number
4885      */
4886     this.defaultWidth = 100;
4887
4888     /**
4889      * Default sortable of columns which have no sortable specified (defaults to false)
4890      * @type Boolean
4891      */
4892     this.defaultSortable = false;
4893
4894     this.addEvents({
4895         /**
4896              * @event widthchange
4897              * Fires when the width of a column changes.
4898              * @param {ColumnModel} this
4899              * @param {Number} columnIndex The column index
4900              * @param {Number} newWidth The new width
4901              */
4902             "widthchange": true,
4903         /**
4904              * @event headerchange
4905              * Fires when the text of a header changes.
4906              * @param {ColumnModel} this
4907              * @param {Number} columnIndex The column index
4908              * @param {Number} newText The new header text
4909              */
4910             "headerchange": true,
4911         /**
4912              * @event hiddenchange
4913              * Fires when a column is hidden or "unhidden".
4914              * @param {ColumnModel} this
4915              * @param {Number} columnIndex The column index
4916              * @param {Boolean} hidden true if hidden, false otherwise
4917              */
4918             "hiddenchange": true,
4919             /**
4920          * @event columnmoved
4921          * Fires when a column is moved.
4922          * @param {ColumnModel} this
4923          * @param {Number} oldIndex
4924          * @param {Number} newIndex
4925          */
4926         "columnmoved" : true,
4927         /**
4928          * @event columlockchange
4929          * Fires when a column's locked state is changed
4930          * @param {ColumnModel} this
4931          * @param {Number} colIndex
4932          * @param {Boolean} locked true if locked
4933          */
4934         "columnlockchange" : true
4935     });
4936     Roo.grid.ColumnModel.superclass.constructor.call(this);
4937 };
4938 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4939     /**
4940      * @cfg {String} header The header text to display in the Grid view.
4941      */
4942     /**
4943      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4944      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4945      * specified, the column's index is used as an index into the Record's data Array.
4946      */
4947     /**
4948      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4949      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4950      */
4951     /**
4952      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4953      * Defaults to the value of the {@link #defaultSortable} property.
4954      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4955      */
4956     /**
4957      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4958      */
4959     /**
4960      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4961      */
4962     /**
4963      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4964      */
4965     /**
4966      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4967      */
4968     /**
4969      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4970      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4971      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4972      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4973      */
4974        /**
4975      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4976      */
4977     /**
4978      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4979      */
4980     /**
4981      * @cfg {String} cursor (Optional)
4982      */
4983     /**
4984      * @cfg {String} tooltip (Optional)
4985      */
4986     /**
4987      * @cfg {Number} xs (Optional)
4988      */
4989     /**
4990      * @cfg {Number} sm (Optional)
4991      */
4992     /**
4993      * @cfg {Number} md (Optional)
4994      */
4995     /**
4996      * @cfg {Number} lg (Optional)
4997      */
4998     /**
4999      * Returns the id of the column at the specified index.
5000      * @param {Number} index The column index
5001      * @return {String} the id
5002      */
5003     getColumnId : function(index){
5004         return this.config[index].id;
5005     },
5006
5007     /**
5008      * Returns the column for a specified id.
5009      * @param {String} id The column id
5010      * @return {Object} the column
5011      */
5012     getColumnById : function(id){
5013         return this.lookup[id];
5014     },
5015
5016     
5017     /**
5018      * Returns the column for a specified dataIndex.
5019      * @param {String} dataIndex The column dataIndex
5020      * @return {Object|Boolean} the column or false if not found
5021      */
5022     getColumnByDataIndex: function(dataIndex){
5023         var index = this.findColumnIndex(dataIndex);
5024         return index > -1 ? this.config[index] : false;
5025     },
5026     
5027     /**
5028      * Returns the index for a specified column id.
5029      * @param {String} id The column id
5030      * @return {Number} the index, or -1 if not found
5031      */
5032     getIndexById : function(id){
5033         for(var i = 0, len = this.config.length; i < len; i++){
5034             if(this.config[i].id == id){
5035                 return i;
5036             }
5037         }
5038         return -1;
5039     },
5040     
5041     /**
5042      * Returns the index for a specified column dataIndex.
5043      * @param {String} dataIndex The column dataIndex
5044      * @return {Number} the index, or -1 if not found
5045      */
5046     
5047     findColumnIndex : function(dataIndex){
5048         for(var i = 0, len = this.config.length; i < len; i++){
5049             if(this.config[i].dataIndex == dataIndex){
5050                 return i;
5051             }
5052         }
5053         return -1;
5054     },
5055     
5056     
5057     moveColumn : function(oldIndex, newIndex){
5058         var c = this.config[oldIndex];
5059         this.config.splice(oldIndex, 1);
5060         this.config.splice(newIndex, 0, c);
5061         this.dataMap = null;
5062         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5063     },
5064
5065     isLocked : function(colIndex){
5066         return this.config[colIndex].locked === true;
5067     },
5068
5069     setLocked : function(colIndex, value, suppressEvent){
5070         if(this.isLocked(colIndex) == value){
5071             return;
5072         }
5073         this.config[colIndex].locked = value;
5074         if(!suppressEvent){
5075             this.fireEvent("columnlockchange", this, colIndex, value);
5076         }
5077     },
5078
5079     getTotalLockedWidth : function(){
5080         var totalWidth = 0;
5081         for(var i = 0; i < this.config.length; i++){
5082             if(this.isLocked(i) && !this.isHidden(i)){
5083                 this.totalWidth += this.getColumnWidth(i);
5084             }
5085         }
5086         return totalWidth;
5087     },
5088
5089     getLockedCount : function(){
5090         for(var i = 0, len = this.config.length; i < len; i++){
5091             if(!this.isLocked(i)){
5092                 return i;
5093             }
5094         }
5095     },
5096
5097     /**
5098      * Returns the number of columns.
5099      * @return {Number}
5100      */
5101     getColumnCount : function(visibleOnly){
5102         if(visibleOnly === true){
5103             var c = 0;
5104             for(var i = 0, len = this.config.length; i < len; i++){
5105                 if(!this.isHidden(i)){
5106                     c++;
5107                 }
5108             }
5109             return c;
5110         }
5111         return this.config.length;
5112     },
5113
5114     /**
5115      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5116      * @param {Function} fn
5117      * @param {Object} scope (optional)
5118      * @return {Array} result
5119      */
5120     getColumnsBy : function(fn, scope){
5121         var r = [];
5122         for(var i = 0, len = this.config.length; i < len; i++){
5123             var c = this.config[i];
5124             if(fn.call(scope||this, c, i) === true){
5125                 r[r.length] = c;
5126             }
5127         }
5128         return r;
5129     },
5130
5131     /**
5132      * Returns true if the specified column is sortable.
5133      * @param {Number} col The column index
5134      * @return {Boolean}
5135      */
5136     isSortable : function(col){
5137         if(typeof this.config[col].sortable == "undefined"){
5138             return this.defaultSortable;
5139         }
5140         return this.config[col].sortable;
5141     },
5142
5143     /**
5144      * Returns the rendering (formatting) function defined for the column.
5145      * @param {Number} col The column index.
5146      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5147      */
5148     getRenderer : function(col){
5149         if(!this.config[col].renderer){
5150             return Roo.grid.ColumnModel.defaultRenderer;
5151         }
5152         return this.config[col].renderer;
5153     },
5154
5155     /**
5156      * Sets the rendering (formatting) function for a column.
5157      * @param {Number} col The column index
5158      * @param {Function} fn The function to use to process the cell's raw data
5159      * to return HTML markup for the grid view. The render function is called with
5160      * the following parameters:<ul>
5161      * <li>Data value.</li>
5162      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5163      * <li>css A CSS style string to apply to the table cell.</li>
5164      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5165      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5166      * <li>Row index</li>
5167      * <li>Column index</li>
5168      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5169      */
5170     setRenderer : function(col, fn){
5171         this.config[col].renderer = fn;
5172     },
5173
5174     /**
5175      * Returns the width for the specified column.
5176      * @param {Number} col The column index
5177      * @return {Number}
5178      */
5179     getColumnWidth : function(col){
5180         return this.config[col].width * 1 || this.defaultWidth;
5181     },
5182
5183     /**
5184      * Sets the width for a column.
5185      * @param {Number} col The column index
5186      * @param {Number} width The new width
5187      */
5188     setColumnWidth : function(col, width, suppressEvent){
5189         this.config[col].width = width;
5190         this.totalWidth = null;
5191         if(!suppressEvent){
5192              this.fireEvent("widthchange", this, col, width);
5193         }
5194     },
5195
5196     /**
5197      * Returns the total width of all columns.
5198      * @param {Boolean} includeHidden True to include hidden column widths
5199      * @return {Number}
5200      */
5201     getTotalWidth : function(includeHidden){
5202         if(!this.totalWidth){
5203             this.totalWidth = 0;
5204             for(var i = 0, len = this.config.length; i < len; i++){
5205                 if(includeHidden || !this.isHidden(i)){
5206                     this.totalWidth += this.getColumnWidth(i);
5207                 }
5208             }
5209         }
5210         return this.totalWidth;
5211     },
5212
5213     /**
5214      * Returns the header for the specified column.
5215      * @param {Number} col The column index
5216      * @return {String}
5217      */
5218     getColumnHeader : function(col){
5219         return this.config[col].header;
5220     },
5221
5222     /**
5223      * Sets the header for a column.
5224      * @param {Number} col The column index
5225      * @param {String} header The new header
5226      */
5227     setColumnHeader : function(col, header){
5228         this.config[col].header = header;
5229         this.fireEvent("headerchange", this, col, header);
5230     },
5231
5232     /**
5233      * Returns the tooltip for the specified column.
5234      * @param {Number} col The column index
5235      * @return {String}
5236      */
5237     getColumnTooltip : function(col){
5238             return this.config[col].tooltip;
5239     },
5240     /**
5241      * Sets the tooltip for a column.
5242      * @param {Number} col The column index
5243      * @param {String} tooltip The new tooltip
5244      */
5245     setColumnTooltip : function(col, tooltip){
5246             this.config[col].tooltip = tooltip;
5247     },
5248
5249     /**
5250      * Returns the dataIndex for the specified column.
5251      * @param {Number} col The column index
5252      * @return {Number}
5253      */
5254     getDataIndex : function(col){
5255         return this.config[col].dataIndex;
5256     },
5257
5258     /**
5259      * Sets the dataIndex for a column.
5260      * @param {Number} col The column index
5261      * @param {Number} dataIndex The new dataIndex
5262      */
5263     setDataIndex : function(col, dataIndex){
5264         this.config[col].dataIndex = dataIndex;
5265     },
5266
5267     
5268     
5269     /**
5270      * Returns true if the cell is editable.
5271      * @param {Number} colIndex The column index
5272      * @param {Number} rowIndex The row index
5273      * @return {Boolean}
5274      */
5275     isCellEditable : function(colIndex, rowIndex){
5276         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5277     },
5278
5279     /**
5280      * Returns the editor defined for the cell/column.
5281      * return false or null to disable editing.
5282      * @param {Number} colIndex The column index
5283      * @param {Number} rowIndex The row index
5284      * @return {Object}
5285      */
5286     getCellEditor : function(colIndex, rowIndex){
5287         return this.config[colIndex].editor;
5288     },
5289
5290     /**
5291      * Sets if a column is editable.
5292      * @param {Number} col The column index
5293      * @param {Boolean} editable True if the column is editable
5294      */
5295     setEditable : function(col, editable){
5296         this.config[col].editable = editable;
5297     },
5298
5299
5300     /**
5301      * Returns true if the column is hidden.
5302      * @param {Number} colIndex The column index
5303      * @return {Boolean}
5304      */
5305     isHidden : function(colIndex){
5306         return this.config[colIndex].hidden;
5307     },
5308
5309
5310     /**
5311      * Returns true if the column width cannot be changed
5312      */
5313     isFixed : function(colIndex){
5314         return this.config[colIndex].fixed;
5315     },
5316
5317     /**
5318      * Returns true if the column can be resized
5319      * @return {Boolean}
5320      */
5321     isResizable : function(colIndex){
5322         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5323     },
5324     /**
5325      * Sets if a column is hidden.
5326      * @param {Number} colIndex The column index
5327      * @param {Boolean} hidden True if the column is hidden
5328      */
5329     setHidden : function(colIndex, hidden){
5330         this.config[colIndex].hidden = hidden;
5331         this.totalWidth = null;
5332         this.fireEvent("hiddenchange", this, colIndex, hidden);
5333     },
5334
5335     /**
5336      * Sets the editor for a column.
5337      * @param {Number} col The column index
5338      * @param {Object} editor The editor object
5339      */
5340     setEditor : function(col, editor){
5341         this.config[col].editor = editor;
5342     }
5343 });
5344
5345 Roo.grid.ColumnModel.defaultRenderer = function(value){
5346         if(typeof value == "string" && value.length < 1){
5347             return "&#160;";
5348         }
5349         return value;
5350 };
5351
5352 // Alias for backwards compatibility
5353 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5354 /*
5355  * Based on:
5356  * Ext JS Library 1.1.1
5357  * Copyright(c) 2006-2007, Ext JS, LLC.
5358  *
5359  * Originally Released Under LGPL - original licence link has changed is not relivant.
5360  *
5361  * Fork - LGPL
5362  * <script type="text/javascript">
5363  */
5364  
5365 /**
5366  * @class Roo.LoadMask
5367  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5368  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5369  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5370  * element's UpdateManager load indicator and will be destroyed after the initial load.
5371  * @constructor
5372  * Create a new LoadMask
5373  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5374  * @param {Object} config The config object
5375  */
5376 Roo.LoadMask = function(el, config){
5377     this.el = Roo.get(el);
5378     Roo.apply(this, config);
5379     if(this.store){
5380         this.store.on('beforeload', this.onBeforeLoad, this);
5381         this.store.on('load', this.onLoad, this);
5382         this.store.on('loadexception', this.onLoadException, this);
5383         this.removeMask = false;
5384     }else{
5385         var um = this.el.getUpdateManager();
5386         um.showLoadIndicator = false; // disable the default indicator
5387         um.on('beforeupdate', this.onBeforeLoad, this);
5388         um.on('update', this.onLoad, this);
5389         um.on('failure', this.onLoad, this);
5390         this.removeMask = true;
5391     }
5392 };
5393
5394 Roo.LoadMask.prototype = {
5395     /**
5396      * @cfg {Boolean} removeMask
5397      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5398      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5399      */
5400     /**
5401      * @cfg {String} msg
5402      * The text to display in a centered loading message box (defaults to 'Loading...')
5403      */
5404     msg : 'Loading...',
5405     /**
5406      * @cfg {String} msgCls
5407      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5408      */
5409     msgCls : 'x-mask-loading',
5410
5411     /**
5412      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5413      * @type Boolean
5414      */
5415     disabled: false,
5416
5417     /**
5418      * Disables the mask to prevent it from being displayed
5419      */
5420     disable : function(){
5421        this.disabled = true;
5422     },
5423
5424     /**
5425      * Enables the mask so that it can be displayed
5426      */
5427     enable : function(){
5428         this.disabled = false;
5429     },
5430     
5431     onLoadException : function()
5432     {
5433         Roo.log(arguments);
5434         
5435         if (typeof(arguments[3]) != 'undefined') {
5436             Roo.MessageBox.alert("Error loading",arguments[3]);
5437         } 
5438         /*
5439         try {
5440             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5441                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5442             }   
5443         } catch(e) {
5444             
5445         }
5446         */
5447     
5448         
5449         
5450         this.el.unmask(this.removeMask);
5451     },
5452     // private
5453     onLoad : function()
5454     {
5455         this.el.unmask(this.removeMask);
5456     },
5457
5458     // private
5459     onBeforeLoad : function(){
5460         if(!this.disabled){
5461             this.el.mask(this.msg, this.msgCls);
5462         }
5463     },
5464
5465     // private
5466     destroy : function(){
5467         if(this.store){
5468             this.store.un('beforeload', this.onBeforeLoad, this);
5469             this.store.un('load', this.onLoad, this);
5470             this.store.un('loadexception', this.onLoadException, this);
5471         }else{
5472             var um = this.el.getUpdateManager();
5473             um.un('beforeupdate', this.onBeforeLoad, this);
5474             um.un('update', this.onLoad, this);
5475             um.un('failure', this.onLoad, this);
5476         }
5477     }
5478 };/*
5479  * - LGPL
5480  *
5481  * table
5482  * 
5483  */
5484
5485 /**
5486  * @class Roo.bootstrap.Table
5487  * @extends Roo.bootstrap.Component
5488  * Bootstrap Table class
5489  * @cfg {String} cls table class
5490  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5491  * @cfg {String} bgcolor Specifies the background color for a table
5492  * @cfg {Number} border Specifies whether the table cells should have borders or not
5493  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5494  * @cfg {Number} cellspacing Specifies the space between cells
5495  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5496  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5497  * @cfg {String} sortable Specifies that the table should be sortable
5498  * @cfg {String} summary Specifies a summary of the content of a table
5499  * @cfg {Number} width Specifies the width of a table
5500  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5501  * 
5502  * @cfg {boolean} striped Should the rows be alternative striped
5503  * @cfg {boolean} bordered Add borders to the table
5504  * @cfg {boolean} hover Add hover highlighting
5505  * @cfg {boolean} condensed Format condensed
5506  * @cfg {boolean} responsive Format condensed
5507  * @cfg {Boolean} loadMask (true|false) default false
5508  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5509  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5510  * @cfg {Boolean} rowSelection (true|false) default false
5511  * @cfg {Boolean} cellSelection (true|false) default false
5512  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5513  
5514  * 
5515  * @constructor
5516  * Create a new Table
5517  * @param {Object} config The config object
5518  */
5519
5520 Roo.bootstrap.Table = function(config){
5521     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5522     
5523     // BC...
5524     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5525     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5526     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5527     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5528     
5529     
5530     if (this.sm) {
5531         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5532         this.sm = this.selModel;
5533         this.sm.xmodule = this.xmodule || false;
5534     }
5535     if (this.cm && typeof(this.cm.config) == 'undefined') {
5536         this.colModel = new Roo.grid.ColumnModel(this.cm);
5537         this.cm = this.colModel;
5538         this.cm.xmodule = this.xmodule || false;
5539     }
5540     if (this.store) {
5541         this.store= Roo.factory(this.store, Roo.data);
5542         this.ds = this.store;
5543         this.ds.xmodule = this.xmodule || false;
5544          
5545     }
5546     if (this.footer && this.store) {
5547         this.footer.dataSource = this.ds;
5548         this.footer = Roo.factory(this.footer);
5549     }
5550     
5551     /** @private */
5552     this.addEvents({
5553         /**
5554          * @event cellclick
5555          * Fires when a cell is clicked
5556          * @param {Roo.bootstrap.Table} this
5557          * @param {Roo.Element} el
5558          * @param {Number} rowIndex
5559          * @param {Number} columnIndex
5560          * @param {Roo.EventObject} e
5561          */
5562         "cellclick" : true,
5563         /**
5564          * @event celldblclick
5565          * Fires when a cell is double clicked
5566          * @param {Roo.bootstrap.Table} this
5567          * @param {Roo.Element} el
5568          * @param {Number} rowIndex
5569          * @param {Number} columnIndex
5570          * @param {Roo.EventObject} e
5571          */
5572         "celldblclick" : true,
5573         /**
5574          * @event rowclick
5575          * Fires when a row is clicked
5576          * @param {Roo.bootstrap.Table} this
5577          * @param {Roo.Element} el
5578          * @param {Number} rowIndex
5579          * @param {Roo.EventObject} e
5580          */
5581         "rowclick" : true,
5582         /**
5583          * @event rowdblclick
5584          * Fires when a row is double clicked
5585          * @param {Roo.bootstrap.Table} this
5586          * @param {Roo.Element} el
5587          * @param {Number} rowIndex
5588          * @param {Roo.EventObject} e
5589          */
5590         "rowdblclick" : true,
5591         /**
5592          * @event mouseover
5593          * Fires when a mouseover occur
5594          * @param {Roo.bootstrap.Table} this
5595          * @param {Roo.Element} el
5596          * @param {Number} rowIndex
5597          * @param {Number} columnIndex
5598          * @param {Roo.EventObject} e
5599          */
5600         "mouseover" : true,
5601         /**
5602          * @event mouseout
5603          * Fires when a mouseout occur
5604          * @param {Roo.bootstrap.Table} this
5605          * @param {Roo.Element} el
5606          * @param {Number} rowIndex
5607          * @param {Number} columnIndex
5608          * @param {Roo.EventObject} e
5609          */
5610         "mouseout" : true,
5611         /**
5612          * @event rowclass
5613          * Fires when a row is rendered, so you can change add a style to it.
5614          * @param {Roo.bootstrap.Table} this
5615          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5616          */
5617         'rowclass' : true,
5618           /**
5619          * @event rowsrendered
5620          * Fires when all the  rows have been rendered
5621          * @param {Roo.bootstrap.Table} this
5622          */
5623         'rowsrendered' : true
5624         
5625     });
5626 };
5627
5628 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5629     
5630     cls: false,
5631     align: false,
5632     bgcolor: false,
5633     border: false,
5634     cellpadding: false,
5635     cellspacing: false,
5636     frame: false,
5637     rules: false,
5638     sortable: false,
5639     summary: false,
5640     width: false,
5641     striped : false,
5642     bordered: false,
5643     hover:  false,
5644     condensed : false,
5645     responsive : false,
5646     sm : false,
5647     cm : false,
5648     store : false,
5649     loadMask : false,
5650     footerShow : true,
5651     headerShow : true,
5652   
5653     rowSelection : false,
5654     cellSelection : false,
5655     layout : false,
5656     
5657     // Roo.Element - the tbody
5658     mainBody: false, 
5659     
5660     getAutoCreate : function(){
5661         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5662         
5663         cfg = {
5664             tag: 'table',
5665             cls : 'table',
5666             cn : []
5667         };
5668             
5669         if (this.striped) {
5670             cfg.cls += ' table-striped';
5671         }
5672         
5673         if (this.hover) {
5674             cfg.cls += ' table-hover';
5675         }
5676         if (this.bordered) {
5677             cfg.cls += ' table-bordered';
5678         }
5679         if (this.condensed) {
5680             cfg.cls += ' table-condensed';
5681         }
5682         if (this.responsive) {
5683             cfg.cls += ' table-responsive';
5684         }
5685         
5686         if (this.cls) {
5687             cfg.cls+=  ' ' +this.cls;
5688         }
5689         
5690         // this lot should be simplifed...
5691         
5692         if (this.align) {
5693             cfg.align=this.align;
5694         }
5695         if (this.bgcolor) {
5696             cfg.bgcolor=this.bgcolor;
5697         }
5698         if (this.border) {
5699             cfg.border=this.border;
5700         }
5701         if (this.cellpadding) {
5702             cfg.cellpadding=this.cellpadding;
5703         }
5704         if (this.cellspacing) {
5705             cfg.cellspacing=this.cellspacing;
5706         }
5707         if (this.frame) {
5708             cfg.frame=this.frame;
5709         }
5710         if (this.rules) {
5711             cfg.rules=this.rules;
5712         }
5713         if (this.sortable) {
5714             cfg.sortable=this.sortable;
5715         }
5716         if (this.summary) {
5717             cfg.summary=this.summary;
5718         }
5719         if (this.width) {
5720             cfg.width=this.width;
5721         }
5722         if (this.layout) {
5723             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5724         }
5725         
5726         if(this.store || this.cm){
5727             if(this.headerShow){
5728                 cfg.cn.push(this.renderHeader());
5729             }
5730             
5731             cfg.cn.push(this.renderBody());
5732             
5733             if(this.footerShow){
5734                 cfg.cn.push(this.renderFooter());
5735             }
5736             
5737             cfg.cls+=  ' TableGrid';
5738         }
5739         
5740         return { cn : [ cfg ] };
5741     },
5742     
5743     initEvents : function()
5744     {   
5745         if(!this.store || !this.cm){
5746             return;
5747         }
5748         
5749         //Roo.log('initEvents with ds!!!!');
5750         
5751         this.mainBody = this.el.select('tbody', true).first();
5752         
5753         
5754         var _this = this;
5755         
5756         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5757             e.on('click', _this.sort, _this);
5758         });
5759         
5760         this.el.on("click", this.onClick, this);
5761         this.el.on("dblclick", this.onDblClick, this);
5762         
5763         // why is this done????? = it breaks dialogs??
5764         //this.parent().el.setStyle('position', 'relative');
5765         
5766         
5767         if (this.footer) {
5768             this.footer.parentId = this.id;
5769             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5770         }
5771         
5772         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5773         
5774         this.store.on('load', this.onLoad, this);
5775         this.store.on('beforeload', this.onBeforeLoad, this);
5776         this.store.on('update', this.onUpdate, this);
5777         this.store.on('add', this.onAdd, this);
5778         
5779     },
5780     
5781     onMouseover : function(e, el)
5782     {
5783         var cell = Roo.get(el);
5784         
5785         if(!cell){
5786             return;
5787         }
5788         
5789         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5790             cell = cell.findParent('td', false, true);
5791         }
5792         
5793         var row = cell.findParent('tr', false, true);
5794         var cellIndex = cell.dom.cellIndex;
5795         var rowIndex = row.dom.rowIndex - 1; // start from 0
5796         
5797         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5798         
5799     },
5800     
5801     onMouseout : function(e, el)
5802     {
5803         var cell = Roo.get(el);
5804         
5805         if(!cell){
5806             return;
5807         }
5808         
5809         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5810             cell = cell.findParent('td', false, true);
5811         }
5812         
5813         var row = cell.findParent('tr', false, true);
5814         var cellIndex = cell.dom.cellIndex;
5815         var rowIndex = row.dom.rowIndex - 1; // start from 0
5816         
5817         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5818         
5819     },
5820     
5821     onClick : function(e, el)
5822     {
5823         var cell = Roo.get(el);
5824         
5825         if(!cell || (!this.cellSelection && !this.rowSelection)){
5826             return;
5827         }
5828         
5829         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5830             cell = cell.findParent('td', false, true);
5831         }
5832         
5833         if(!cell || typeof(cell) == 'undefined'){
5834             return;
5835         }
5836         
5837         var row = cell.findParent('tr', false, true);
5838         
5839         if(!row || typeof(row) == 'undefined'){
5840             return;
5841         }
5842         
5843         var cellIndex = cell.dom.cellIndex;
5844         var rowIndex = this.getRowIndex(row);
5845         
5846         // why??? - should these not be based on SelectionModel?
5847         if(this.cellSelection){
5848             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5849         }
5850         
5851         if(this.rowSelection){
5852             this.fireEvent('rowclick', this, row, rowIndex, e);
5853         }
5854         
5855         
5856     },
5857     
5858     onDblClick : function(e,el)
5859     {
5860         var cell = Roo.get(el);
5861         
5862         if(!cell || (!this.CellSelection && !this.RowSelection)){
5863             return;
5864         }
5865         
5866         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5867             cell = cell.findParent('td', false, true);
5868         }
5869         
5870         if(!cell || typeof(cell) == 'undefined'){
5871             return;
5872         }
5873         
5874         var row = cell.findParent('tr', false, true);
5875         
5876         if(!row || typeof(row) == 'undefined'){
5877             return;
5878         }
5879         
5880         var cellIndex = cell.dom.cellIndex;
5881         var rowIndex = this.getRowIndex(row);
5882         
5883         if(this.CellSelection){
5884             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5885         }
5886         
5887         if(this.RowSelection){
5888             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5889         }
5890     },
5891     
5892     sort : function(e,el)
5893     {
5894         var col = Roo.get(el);
5895         
5896         if(!col.hasClass('sortable')){
5897             return;
5898         }
5899         
5900         var sort = col.attr('sort');
5901         var dir = 'ASC';
5902         
5903         if(col.hasClass('glyphicon-arrow-up')){
5904             dir = 'DESC';
5905         }
5906         
5907         this.store.sortInfo = {field : sort, direction : dir};
5908         
5909         if (this.footer) {
5910             Roo.log("calling footer first");
5911             this.footer.onClick('first');
5912         } else {
5913         
5914             this.store.load({ params : { start : 0 } });
5915         }
5916     },
5917     
5918     renderHeader : function()
5919     {
5920         var header = {
5921             tag: 'thead',
5922             cn : []
5923         };
5924         
5925         var cm = this.cm;
5926         
5927         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5928             
5929             var config = cm.config[i];
5930             
5931             var c = {
5932                 tag: 'th',
5933                 style : '',
5934                 html: cm.getColumnHeader(i)
5935             };
5936             
5937             var hh = '';
5938             
5939             if(typeof(config.lgHeader) != 'undefined'){
5940                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5941             }
5942             
5943             if(typeof(config.mdHeader) != 'undefined'){
5944                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5945             }
5946             
5947             if(typeof(config.smHeader) != 'undefined'){
5948                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5949             }
5950             
5951             if(typeof(config.xsHeader) != 'undefined'){
5952                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5953             }
5954             
5955             if(hh.length){
5956                 c.html = hh;
5957             }
5958             
5959             if(typeof(config.tooltip) != 'undefined'){
5960                 c.tooltip = config.tooltip;
5961             }
5962             
5963             if(typeof(config.colspan) != 'undefined'){
5964                 c.colspan = config.colspan;
5965             }
5966             
5967             if(typeof(config.hidden) != 'undefined' && config.hidden){
5968                 c.style += ' display:none;';
5969             }
5970             
5971             if(typeof(config.dataIndex) != 'undefined'){
5972                 c.sort = config.dataIndex;
5973             }
5974             
5975             if(typeof(config.sortable) != 'undefined' && config.sortable){
5976                 c.cls = 'sortable';
5977             }
5978             
5979             if(typeof(config.align) != 'undefined' && config.align.length){
5980                 c.style += ' text-align:' + config.align + ';';
5981             }
5982             
5983             if(typeof(config.width) != 'undefined'){
5984                 c.style += ' width:' + config.width + 'px;';
5985             }
5986             
5987             if(typeof(config.cls) != 'undefined'){
5988                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5989             }
5990             
5991             ['xs','sm','md','lg'].map(function(size){
5992                 
5993                 if(typeof(config[size]) == 'undefined'){
5994                     return;
5995                 }
5996                 
5997                 if (!config[size]) { // 0 = hidden
5998                     cfg.cls += ' hidden-' + size;
5999                     return;
6000                 }
6001                 
6002                 cfg.cls += ' col-' + size + '-' + config[size];
6003
6004             });
6005             
6006             header.cn.push(c)
6007         }
6008         
6009         return header;
6010     },
6011     
6012     renderBody : function()
6013     {
6014         var body = {
6015             tag: 'tbody',
6016             cn : [
6017                 {
6018                     tag: 'tr',
6019                     cn : [
6020                         {
6021                             tag : 'td',
6022                             colspan :  this.cm.getColumnCount()
6023                         }
6024                     ]
6025                 }
6026             ]
6027         };
6028         
6029         return body;
6030     },
6031     
6032     renderFooter : function()
6033     {
6034         var footer = {
6035             tag: 'tfoot',
6036             cn : [
6037                 {
6038                     tag: 'tr',
6039                     cn : [
6040                         {
6041                             tag : 'td',
6042                             colspan :  this.cm.getColumnCount()
6043                         }
6044                     ]
6045                 }
6046             ]
6047         };
6048         
6049         return footer;
6050     },
6051     
6052     
6053     
6054     onLoad : function()
6055     {
6056         Roo.log('ds onload');
6057         this.clear();
6058         
6059         var _this = this;
6060         var cm = this.cm;
6061         var ds = this.store;
6062         
6063         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6064             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6065             
6066             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6067                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6068             }
6069             
6070             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6071                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6072             }
6073         });
6074         
6075         var tbody =  this.mainBody;
6076               
6077         if(ds.getCount() > 0){
6078             ds.data.each(function(d,rowIndex){
6079                 var row =  this.renderRow(cm, ds, rowIndex);
6080                 
6081                 tbody.createChild(row);
6082                 
6083                 var _this = this;
6084                 
6085                 if(row.cellObjects.length){
6086                     Roo.each(row.cellObjects, function(r){
6087                         _this.renderCellObject(r);
6088                     })
6089                 }
6090                 
6091             }, this);
6092         }
6093         
6094         Roo.each(this.el.select('tbody td', true).elements, function(e){
6095             e.on('mouseover', _this.onMouseover, _this);
6096         });
6097         
6098         Roo.each(this.el.select('tbody td', true).elements, function(e){
6099             e.on('mouseout', _this.onMouseout, _this);
6100         });
6101         this.fireEvent('rowsrendered', this);
6102         //if(this.loadMask){
6103         //    this.maskEl.hide();
6104         //}
6105     },
6106     
6107     
6108     onUpdate : function(ds,record)
6109     {
6110         this.refreshRow(record);
6111     },
6112     
6113     onRemove : function(ds, record, index, isUpdate){
6114         if(isUpdate !== true){
6115             this.fireEvent("beforerowremoved", this, index, record);
6116         }
6117         var bt = this.mainBody.dom;
6118         
6119         var rows = this.el.select('tbody > tr', true).elements;
6120         
6121         if(typeof(rows[index]) != 'undefined'){
6122             bt.removeChild(rows[index].dom);
6123         }
6124         
6125 //        if(bt.rows[index]){
6126 //            bt.removeChild(bt.rows[index]);
6127 //        }
6128         
6129         if(isUpdate !== true){
6130             //this.stripeRows(index);
6131             //this.syncRowHeights(index, index);
6132             //this.layout();
6133             this.fireEvent("rowremoved", this, index, record);
6134         }
6135     },
6136     
6137     onAdd : function(ds, records, rowIndex)
6138     {
6139         //Roo.log('on Add called');
6140         // - note this does not handle multiple adding very well..
6141         var bt = this.mainBody.dom;
6142         for (var i =0 ; i < records.length;i++) {
6143             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6144             //Roo.log(records[i]);
6145             //Roo.log(this.store.getAt(rowIndex+i));
6146             this.insertRow(this.store, rowIndex + i, false);
6147             return;
6148         }
6149         
6150     },
6151     
6152     
6153     refreshRow : function(record){
6154         var ds = this.store, index;
6155         if(typeof record == 'number'){
6156             index = record;
6157             record = ds.getAt(index);
6158         }else{
6159             index = ds.indexOf(record);
6160         }
6161         this.insertRow(ds, index, true);
6162         this.onRemove(ds, record, index+1, true);
6163         //this.syncRowHeights(index, index);
6164         //this.layout();
6165         this.fireEvent("rowupdated", this, index, record);
6166     },
6167     
6168     insertRow : function(dm, rowIndex, isUpdate){
6169         
6170         if(!isUpdate){
6171             this.fireEvent("beforerowsinserted", this, rowIndex);
6172         }
6173             //var s = this.getScrollState();
6174         var row = this.renderRow(this.cm, this.store, rowIndex);
6175         // insert before rowIndex..
6176         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6177         
6178         var _this = this;
6179                 
6180         if(row.cellObjects.length){
6181             Roo.each(row.cellObjects, function(r){
6182                 _this.renderCellObject(r);
6183             })
6184         }
6185             
6186         if(!isUpdate){
6187             this.fireEvent("rowsinserted", this, rowIndex);
6188             //this.syncRowHeights(firstRow, lastRow);
6189             //this.stripeRows(firstRow);
6190             //this.layout();
6191         }
6192         
6193     },
6194     
6195     
6196     getRowDom : function(rowIndex)
6197     {
6198         var rows = this.el.select('tbody > tr', true).elements;
6199         
6200         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6201         
6202     },
6203     // returns the object tree for a tr..
6204   
6205     
6206     renderRow : function(cm, ds, rowIndex) 
6207     {
6208         
6209         var d = ds.getAt(rowIndex);
6210         
6211         var row = {
6212             tag : 'tr',
6213             cn : []
6214         };
6215             
6216         var cellObjects = [];
6217         
6218         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6219             var config = cm.config[i];
6220             
6221             var renderer = cm.getRenderer(i);
6222             var value = '';
6223             var id = false;
6224             
6225             if(typeof(renderer) !== 'undefined'){
6226                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6227             }
6228             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6229             // and are rendered into the cells after the row is rendered - using the id for the element.
6230             
6231             if(typeof(value) === 'object'){
6232                 id = Roo.id();
6233                 cellObjects.push({
6234                     container : id,
6235                     cfg : value 
6236                 })
6237             }
6238             
6239             var rowcfg = {
6240                 record: d,
6241                 rowIndex : rowIndex,
6242                 colIndex : i,
6243                 rowClass : ''
6244             };
6245
6246             this.fireEvent('rowclass', this, rowcfg);
6247             
6248             var td = {
6249                 tag: 'td',
6250                 cls : rowcfg.rowClass,
6251                 style: '',
6252                 html: (typeof(value) === 'object') ? '' : value
6253             };
6254             
6255             if (id) {
6256                 td.id = id;
6257             }
6258             
6259             if(typeof(config.colspan) != 'undefined'){
6260                 td.colspan = config.colspan;
6261             }
6262             
6263             if(typeof(config.hidden) != 'undefined' && config.hidden){
6264                 td.style += ' display:none;';
6265             }
6266             
6267             if(typeof(config.align) != 'undefined' && config.align.length){
6268                 td.style += ' text-align:' + config.align + ';';
6269             }
6270             
6271             if(typeof(config.width) != 'undefined'){
6272                 td.style += ' width:' +  config.width + 'px;';
6273             }
6274             
6275             if(typeof(config.cursor) != 'undefined'){
6276                 td.style += ' cursor:' +  config.cursor + ';';
6277             }
6278             
6279             if(typeof(config.cls) != 'undefined'){
6280                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6281             }
6282             
6283             ['xs','sm','md','lg'].map(function(size){
6284                 
6285                 if(typeof(config[size]) == 'undefined'){
6286                     return;
6287                 }
6288                 
6289                 if (!config[size]) { // 0 = hidden
6290                     td.cls += ' hidden-' + size;
6291                     return;
6292                 }
6293                 
6294                 td.cls += ' col-' + size + '-' + config[size];
6295
6296             });
6297              
6298             row.cn.push(td);
6299            
6300         }
6301         
6302         row.cellObjects = cellObjects;
6303         
6304         return row;
6305           
6306     },
6307     
6308     
6309     
6310     onBeforeLoad : function()
6311     {
6312         //Roo.log('ds onBeforeLoad');
6313         
6314         //this.clear();
6315         
6316         //if(this.loadMask){
6317         //    this.maskEl.show();
6318         //}
6319     },
6320      /**
6321      * Remove all rows
6322      */
6323     clear : function()
6324     {
6325         this.el.select('tbody', true).first().dom.innerHTML = '';
6326     },
6327     /**
6328      * Show or hide a row.
6329      * @param {Number} rowIndex to show or hide
6330      * @param {Boolean} state hide
6331      */
6332     setRowVisibility : function(rowIndex, state)
6333     {
6334         var bt = this.mainBody.dom;
6335         
6336         var rows = this.el.select('tbody > tr', true).elements;
6337         
6338         if(typeof(rows[rowIndex]) == 'undefined'){
6339             return;
6340         }
6341         rows[rowIndex].dom.style.display = state ? '' : 'none';
6342     },
6343     
6344     
6345     getSelectionModel : function(){
6346         if(!this.selModel){
6347             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6348         }
6349         return this.selModel;
6350     },
6351     /*
6352      * Render the Roo.bootstrap object from renderder
6353      */
6354     renderCellObject : function(r)
6355     {
6356         var _this = this;
6357         
6358         var t = r.cfg.render(r.container);
6359         
6360         if(r.cfg.cn){
6361             Roo.each(r.cfg.cn, function(c){
6362                 var child = {
6363                     container: t.getChildContainer(),
6364                     cfg: c
6365                 };
6366                 _this.renderCellObject(child);
6367             })
6368         }
6369     },
6370     
6371     getRowIndex : function(row)
6372     {
6373         var rowIndex = -1;
6374         
6375         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6376             if(el != row){
6377                 return;
6378             }
6379             
6380             rowIndex = index;
6381         });
6382         
6383         return rowIndex;
6384     }
6385    
6386 });
6387
6388  
6389
6390  /*
6391  * - LGPL
6392  *
6393  * table cell
6394  * 
6395  */
6396
6397 /**
6398  * @class Roo.bootstrap.TableCell
6399  * @extends Roo.bootstrap.Component
6400  * Bootstrap TableCell class
6401  * @cfg {String} html cell contain text
6402  * @cfg {String} cls cell class
6403  * @cfg {String} tag cell tag (td|th) default td
6404  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6405  * @cfg {String} align Aligns the content in a cell
6406  * @cfg {String} axis Categorizes cells
6407  * @cfg {String} bgcolor Specifies the background color of a cell
6408  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6409  * @cfg {Number} colspan Specifies the number of columns a cell should span
6410  * @cfg {String} headers Specifies one or more header cells a cell is related to
6411  * @cfg {Number} height Sets the height of a cell
6412  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6413  * @cfg {Number} rowspan Sets the number of rows a cell should span
6414  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6415  * @cfg {String} valign Vertical aligns the content in a cell
6416  * @cfg {Number} width Specifies the width of a cell
6417  * 
6418  * @constructor
6419  * Create a new TableCell
6420  * @param {Object} config The config object
6421  */
6422
6423 Roo.bootstrap.TableCell = function(config){
6424     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6425 };
6426
6427 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6428     
6429     html: false,
6430     cls: false,
6431     tag: false,
6432     abbr: false,
6433     align: false,
6434     axis: false,
6435     bgcolor: false,
6436     charoff: false,
6437     colspan: false,
6438     headers: false,
6439     height: false,
6440     nowrap: false,
6441     rowspan: false,
6442     scope: false,
6443     valign: false,
6444     width: false,
6445     
6446     
6447     getAutoCreate : function(){
6448         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6449         
6450         cfg = {
6451             tag: 'td'
6452         };
6453         
6454         if(this.tag){
6455             cfg.tag = this.tag;
6456         }
6457         
6458         if (this.html) {
6459             cfg.html=this.html
6460         }
6461         if (this.cls) {
6462             cfg.cls=this.cls
6463         }
6464         if (this.abbr) {
6465             cfg.abbr=this.abbr
6466         }
6467         if (this.align) {
6468             cfg.align=this.align
6469         }
6470         if (this.axis) {
6471             cfg.axis=this.axis
6472         }
6473         if (this.bgcolor) {
6474             cfg.bgcolor=this.bgcolor
6475         }
6476         if (this.charoff) {
6477             cfg.charoff=this.charoff
6478         }
6479         if (this.colspan) {
6480             cfg.colspan=this.colspan
6481         }
6482         if (this.headers) {
6483             cfg.headers=this.headers
6484         }
6485         if (this.height) {
6486             cfg.height=this.height
6487         }
6488         if (this.nowrap) {
6489             cfg.nowrap=this.nowrap
6490         }
6491         if (this.rowspan) {
6492             cfg.rowspan=this.rowspan
6493         }
6494         if (this.scope) {
6495             cfg.scope=this.scope
6496         }
6497         if (this.valign) {
6498             cfg.valign=this.valign
6499         }
6500         if (this.width) {
6501             cfg.width=this.width
6502         }
6503         
6504         
6505         return cfg;
6506     }
6507    
6508 });
6509
6510  
6511
6512  /*
6513  * - LGPL
6514  *
6515  * table row
6516  * 
6517  */
6518
6519 /**
6520  * @class Roo.bootstrap.TableRow
6521  * @extends Roo.bootstrap.Component
6522  * Bootstrap TableRow class
6523  * @cfg {String} cls row class
6524  * @cfg {String} align Aligns the content in a table row
6525  * @cfg {String} bgcolor Specifies a background color for a table row
6526  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6527  * @cfg {String} valign Vertical aligns the content in a table row
6528  * 
6529  * @constructor
6530  * Create a new TableRow
6531  * @param {Object} config The config object
6532  */
6533
6534 Roo.bootstrap.TableRow = function(config){
6535     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6536 };
6537
6538 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6539     
6540     cls: false,
6541     align: false,
6542     bgcolor: false,
6543     charoff: false,
6544     valign: false,
6545     
6546     getAutoCreate : function(){
6547         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6548         
6549         cfg = {
6550             tag: 'tr'
6551         };
6552             
6553         if(this.cls){
6554             cfg.cls = this.cls;
6555         }
6556         if(this.align){
6557             cfg.align = this.align;
6558         }
6559         if(this.bgcolor){
6560             cfg.bgcolor = this.bgcolor;
6561         }
6562         if(this.charoff){
6563             cfg.charoff = this.charoff;
6564         }
6565         if(this.valign){
6566             cfg.valign = this.valign;
6567         }
6568         
6569         return cfg;
6570     }
6571    
6572 });
6573
6574  
6575
6576  /*
6577  * - LGPL
6578  *
6579  * table body
6580  * 
6581  */
6582
6583 /**
6584  * @class Roo.bootstrap.TableBody
6585  * @extends Roo.bootstrap.Component
6586  * Bootstrap TableBody class
6587  * @cfg {String} cls element class
6588  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6589  * @cfg {String} align Aligns the content inside the element
6590  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6591  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6592  * 
6593  * @constructor
6594  * Create a new TableBody
6595  * @param {Object} config The config object
6596  */
6597
6598 Roo.bootstrap.TableBody = function(config){
6599     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6600 };
6601
6602 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6603     
6604     cls: false,
6605     tag: false,
6606     align: false,
6607     charoff: false,
6608     valign: false,
6609     
6610     getAutoCreate : function(){
6611         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6612         
6613         cfg = {
6614             tag: 'tbody'
6615         };
6616             
6617         if (this.cls) {
6618             cfg.cls=this.cls
6619         }
6620         if(this.tag){
6621             cfg.tag = this.tag;
6622         }
6623         
6624         if(this.align){
6625             cfg.align = this.align;
6626         }
6627         if(this.charoff){
6628             cfg.charoff = this.charoff;
6629         }
6630         if(this.valign){
6631             cfg.valign = this.valign;
6632         }
6633         
6634         return cfg;
6635     }
6636     
6637     
6638 //    initEvents : function()
6639 //    {
6640 //        
6641 //        if(!this.store){
6642 //            return;
6643 //        }
6644 //        
6645 //        this.store = Roo.factory(this.store, Roo.data);
6646 //        this.store.on('load', this.onLoad, this);
6647 //        
6648 //        this.store.load();
6649 //        
6650 //    },
6651 //    
6652 //    onLoad: function () 
6653 //    {   
6654 //        this.fireEvent('load', this);
6655 //    }
6656 //    
6657 //   
6658 });
6659
6660  
6661
6662  /*
6663  * Based on:
6664  * Ext JS Library 1.1.1
6665  * Copyright(c) 2006-2007, Ext JS, LLC.
6666  *
6667  * Originally Released Under LGPL - original licence link has changed is not relivant.
6668  *
6669  * Fork - LGPL
6670  * <script type="text/javascript">
6671  */
6672
6673 // as we use this in bootstrap.
6674 Roo.namespace('Roo.form');
6675  /**
6676  * @class Roo.form.Action
6677  * Internal Class used to handle form actions
6678  * @constructor
6679  * @param {Roo.form.BasicForm} el The form element or its id
6680  * @param {Object} config Configuration options
6681  */
6682
6683  
6684  
6685 // define the action interface
6686 Roo.form.Action = function(form, options){
6687     this.form = form;
6688     this.options = options || {};
6689 };
6690 /**
6691  * Client Validation Failed
6692  * @const 
6693  */
6694 Roo.form.Action.CLIENT_INVALID = 'client';
6695 /**
6696  * Server Validation Failed
6697  * @const 
6698  */
6699 Roo.form.Action.SERVER_INVALID = 'server';
6700  /**
6701  * Connect to Server Failed
6702  * @const 
6703  */
6704 Roo.form.Action.CONNECT_FAILURE = 'connect';
6705 /**
6706  * Reading Data from Server Failed
6707  * @const 
6708  */
6709 Roo.form.Action.LOAD_FAILURE = 'load';
6710
6711 Roo.form.Action.prototype = {
6712     type : 'default',
6713     failureType : undefined,
6714     response : undefined,
6715     result : undefined,
6716
6717     // interface method
6718     run : function(options){
6719
6720     },
6721
6722     // interface method
6723     success : function(response){
6724
6725     },
6726
6727     // interface method
6728     handleResponse : function(response){
6729
6730     },
6731
6732     // default connection failure
6733     failure : function(response){
6734         
6735         this.response = response;
6736         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6737         this.form.afterAction(this, false);
6738     },
6739
6740     processResponse : function(response){
6741         this.response = response;
6742         if(!response.responseText){
6743             return true;
6744         }
6745         this.result = this.handleResponse(response);
6746         return this.result;
6747     },
6748
6749     // utility functions used internally
6750     getUrl : function(appendParams){
6751         var url = this.options.url || this.form.url || this.form.el.dom.action;
6752         if(appendParams){
6753             var p = this.getParams();
6754             if(p){
6755                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6756             }
6757         }
6758         return url;
6759     },
6760
6761     getMethod : function(){
6762         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6763     },
6764
6765     getParams : function(){
6766         var bp = this.form.baseParams;
6767         var p = this.options.params;
6768         if(p){
6769             if(typeof p == "object"){
6770                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6771             }else if(typeof p == 'string' && bp){
6772                 p += '&' + Roo.urlEncode(bp);
6773             }
6774         }else if(bp){
6775             p = Roo.urlEncode(bp);
6776         }
6777         return p;
6778     },
6779
6780     createCallback : function(){
6781         return {
6782             success: this.success,
6783             failure: this.failure,
6784             scope: this,
6785             timeout: (this.form.timeout*1000),
6786             upload: this.form.fileUpload ? this.success : undefined
6787         };
6788     }
6789 };
6790
6791 Roo.form.Action.Submit = function(form, options){
6792     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6793 };
6794
6795 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6796     type : 'submit',
6797
6798     haveProgress : false,
6799     uploadComplete : false,
6800     
6801     // uploadProgress indicator.
6802     uploadProgress : function()
6803     {
6804         if (!this.form.progressUrl) {
6805             return;
6806         }
6807         
6808         if (!this.haveProgress) {
6809             Roo.MessageBox.progress("Uploading", "Uploading");
6810         }
6811         if (this.uploadComplete) {
6812            Roo.MessageBox.hide();
6813            return;
6814         }
6815         
6816         this.haveProgress = true;
6817    
6818         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6819         
6820         var c = new Roo.data.Connection();
6821         c.request({
6822             url : this.form.progressUrl,
6823             params: {
6824                 id : uid
6825             },
6826             method: 'GET',
6827             success : function(req){
6828                //console.log(data);
6829                 var rdata = false;
6830                 var edata;
6831                 try  {
6832                    rdata = Roo.decode(req.responseText)
6833                 } catch (e) {
6834                     Roo.log("Invalid data from server..");
6835                     Roo.log(edata);
6836                     return;
6837                 }
6838                 if (!rdata || !rdata.success) {
6839                     Roo.log(rdata);
6840                     Roo.MessageBox.alert(Roo.encode(rdata));
6841                     return;
6842                 }
6843                 var data = rdata.data;
6844                 
6845                 if (this.uploadComplete) {
6846                    Roo.MessageBox.hide();
6847                    return;
6848                 }
6849                    
6850                 if (data){
6851                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6852                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6853                     );
6854                 }
6855                 this.uploadProgress.defer(2000,this);
6856             },
6857        
6858             failure: function(data) {
6859                 Roo.log('progress url failed ');
6860                 Roo.log(data);
6861             },
6862             scope : this
6863         });
6864            
6865     },
6866     
6867     
6868     run : function()
6869     {
6870         // run get Values on the form, so it syncs any secondary forms.
6871         this.form.getValues();
6872         
6873         var o = this.options;
6874         var method = this.getMethod();
6875         var isPost = method == 'POST';
6876         if(o.clientValidation === false || this.form.isValid()){
6877             
6878             if (this.form.progressUrl) {
6879                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6880                     (new Date() * 1) + '' + Math.random());
6881                     
6882             } 
6883             
6884             
6885             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6886                 form:this.form.el.dom,
6887                 url:this.getUrl(!isPost),
6888                 method: method,
6889                 params:isPost ? this.getParams() : null,
6890                 isUpload: this.form.fileUpload
6891             }));
6892             
6893             this.uploadProgress();
6894
6895         }else if (o.clientValidation !== false){ // client validation failed
6896             this.failureType = Roo.form.Action.CLIENT_INVALID;
6897             this.form.afterAction(this, false);
6898         }
6899     },
6900
6901     success : function(response)
6902     {
6903         this.uploadComplete= true;
6904         if (this.haveProgress) {
6905             Roo.MessageBox.hide();
6906         }
6907         
6908         
6909         var result = this.processResponse(response);
6910         if(result === true || result.success){
6911             this.form.afterAction(this, true);
6912             return;
6913         }
6914         if(result.errors){
6915             this.form.markInvalid(result.errors);
6916             this.failureType = Roo.form.Action.SERVER_INVALID;
6917         }
6918         this.form.afterAction(this, false);
6919     },
6920     failure : function(response)
6921     {
6922         this.uploadComplete= true;
6923         if (this.haveProgress) {
6924             Roo.MessageBox.hide();
6925         }
6926         
6927         this.response = response;
6928         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6929         this.form.afterAction(this, false);
6930     },
6931     
6932     handleResponse : function(response){
6933         if(this.form.errorReader){
6934             var rs = this.form.errorReader.read(response);
6935             var errors = [];
6936             if(rs.records){
6937                 for(var i = 0, len = rs.records.length; i < len; i++) {
6938                     var r = rs.records[i];
6939                     errors[i] = r.data;
6940                 }
6941             }
6942             if(errors.length < 1){
6943                 errors = null;
6944             }
6945             return {
6946                 success : rs.success,
6947                 errors : errors
6948             };
6949         }
6950         var ret = false;
6951         try {
6952             ret = Roo.decode(response.responseText);
6953         } catch (e) {
6954             ret = {
6955                 success: false,
6956                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6957                 errors : []
6958             };
6959         }
6960         return ret;
6961         
6962     }
6963 });
6964
6965
6966 Roo.form.Action.Load = function(form, options){
6967     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6968     this.reader = this.form.reader;
6969 };
6970
6971 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6972     type : 'load',
6973
6974     run : function(){
6975         
6976         Roo.Ajax.request(Roo.apply(
6977                 this.createCallback(), {
6978                     method:this.getMethod(),
6979                     url:this.getUrl(false),
6980                     params:this.getParams()
6981         }));
6982     },
6983
6984     success : function(response){
6985         
6986         var result = this.processResponse(response);
6987         if(result === true || !result.success || !result.data){
6988             this.failureType = Roo.form.Action.LOAD_FAILURE;
6989             this.form.afterAction(this, false);
6990             return;
6991         }
6992         this.form.clearInvalid();
6993         this.form.setValues(result.data);
6994         this.form.afterAction(this, true);
6995     },
6996
6997     handleResponse : function(response){
6998         if(this.form.reader){
6999             var rs = this.form.reader.read(response);
7000             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7001             return {
7002                 success : rs.success,
7003                 data : data
7004             };
7005         }
7006         return Roo.decode(response.responseText);
7007     }
7008 });
7009
7010 Roo.form.Action.ACTION_TYPES = {
7011     'load' : Roo.form.Action.Load,
7012     'submit' : Roo.form.Action.Submit
7013 };/*
7014  * - LGPL
7015  *
7016  * form
7017  * 
7018  */
7019
7020 /**
7021  * @class Roo.bootstrap.Form
7022  * @extends Roo.bootstrap.Component
7023  * Bootstrap Form class
7024  * @cfg {String} method  GET | POST (default POST)
7025  * @cfg {String} labelAlign top | left (default top)
7026  * @cfg {String} align left  | right - for navbars
7027  * @cfg {Boolean} loadMask load mask when submit (default true)
7028
7029  * 
7030  * @constructor
7031  * Create a new Form
7032  * @param {Object} config The config object
7033  */
7034
7035
7036 Roo.bootstrap.Form = function(config){
7037     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7038     this.addEvents({
7039         /**
7040          * @event clientvalidation
7041          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7042          * @param {Form} this
7043          * @param {Boolean} valid true if the form has passed client-side validation
7044          */
7045         clientvalidation: true,
7046         /**
7047          * @event beforeaction
7048          * Fires before any action is performed. Return false to cancel the action.
7049          * @param {Form} this
7050          * @param {Action} action The action to be performed
7051          */
7052         beforeaction: true,
7053         /**
7054          * @event actionfailed
7055          * Fires when an action fails.
7056          * @param {Form} this
7057          * @param {Action} action The action that failed
7058          */
7059         actionfailed : true,
7060         /**
7061          * @event actioncomplete
7062          * Fires when an action is completed.
7063          * @param {Form} this
7064          * @param {Action} action The action that completed
7065          */
7066         actioncomplete : true
7067     });
7068     
7069 };
7070
7071 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7072       
7073      /**
7074      * @cfg {String} method
7075      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7076      */
7077     method : 'POST',
7078     /**
7079      * @cfg {String} url
7080      * The URL to use for form actions if one isn't supplied in the action options.
7081      */
7082     /**
7083      * @cfg {Boolean} fileUpload
7084      * Set to true if this form is a file upload.
7085      */
7086      
7087     /**
7088      * @cfg {Object} baseParams
7089      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7090      */
7091       
7092     /**
7093      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7094      */
7095     timeout: 30,
7096     /**
7097      * @cfg {Sting} align (left|right) for navbar forms
7098      */
7099     align : 'left',
7100
7101     // private
7102     activeAction : null,
7103  
7104     /**
7105      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7106      * element by passing it or its id or mask the form itself by passing in true.
7107      * @type Mixed
7108      */
7109     waitMsgTarget : false,
7110     
7111     loadMask : true,
7112     
7113     getAutoCreate : function(){
7114         
7115         var cfg = {
7116             tag: 'form',
7117             method : this.method || 'POST',
7118             id : this.id || Roo.id(),
7119             cls : ''
7120         };
7121         if (this.parent().xtype.match(/^Nav/)) {
7122             cfg.cls = 'navbar-form navbar-' + this.align;
7123             
7124         }
7125         
7126         if (this.labelAlign == 'left' ) {
7127             cfg.cls += ' form-horizontal';
7128         }
7129         
7130         
7131         return cfg;
7132     },
7133     initEvents : function()
7134     {
7135         this.el.on('submit', this.onSubmit, this);
7136         // this was added as random key presses on the form where triggering form submit.
7137         this.el.on('keypress', function(e) {
7138             if (e.getCharCode() != 13) {
7139                 return true;
7140             }
7141             // we might need to allow it for textareas.. and some other items.
7142             // check e.getTarget().
7143             
7144             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7145                 return true;
7146             }
7147         
7148             Roo.log("keypress blocked");
7149             
7150             e.preventDefault();
7151             return false;
7152         });
7153         
7154     },
7155     // private
7156     onSubmit : function(e){
7157         e.stopEvent();
7158     },
7159     
7160      /**
7161      * Returns true if client-side validation on the form is successful.
7162      * @return Boolean
7163      */
7164     isValid : function(){
7165         var items = this.getItems();
7166         var valid = true;
7167         items.each(function(f){
7168            if(!f.validate()){
7169                valid = false;
7170                
7171            }
7172         });
7173         return valid;
7174     },
7175     /**
7176      * Returns true if any fields in this form have changed since their original load.
7177      * @return Boolean
7178      */
7179     isDirty : function(){
7180         var dirty = false;
7181         var items = this.getItems();
7182         items.each(function(f){
7183            if(f.isDirty()){
7184                dirty = true;
7185                return false;
7186            }
7187            return true;
7188         });
7189         return dirty;
7190     },
7191      /**
7192      * Performs a predefined action (submit or load) or custom actions you define on this form.
7193      * @param {String} actionName The name of the action type
7194      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7195      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7196      * accept other config options):
7197      * <pre>
7198 Property          Type             Description
7199 ----------------  ---------------  ----------------------------------------------------------------------------------
7200 url               String           The url for the action (defaults to the form's url)
7201 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7202 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7203 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7204                                    validate the form on the client (defaults to false)
7205      * </pre>
7206      * @return {BasicForm} this
7207      */
7208     doAction : function(action, options){
7209         if(typeof action == 'string'){
7210             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7211         }
7212         if(this.fireEvent('beforeaction', this, action) !== false){
7213             this.beforeAction(action);
7214             action.run.defer(100, action);
7215         }
7216         return this;
7217     },
7218     
7219     // private
7220     beforeAction : function(action){
7221         var o = action.options;
7222         
7223         if(this.loadMask){
7224             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7225         }
7226         // not really supported yet.. ??
7227         
7228         //if(this.waitMsgTarget === true){
7229         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7230         //}else if(this.waitMsgTarget){
7231         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7232         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7233         //}else {
7234         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7235        // }
7236          
7237     },
7238
7239     // private
7240     afterAction : function(action, success){
7241         this.activeAction = null;
7242         var o = action.options;
7243         
7244         //if(this.waitMsgTarget === true){
7245             this.el.unmask();
7246         //}else if(this.waitMsgTarget){
7247         //    this.waitMsgTarget.unmask();
7248         //}else{
7249         //    Roo.MessageBox.updateProgress(1);
7250         //    Roo.MessageBox.hide();
7251        // }
7252         // 
7253         if(success){
7254             if(o.reset){
7255                 this.reset();
7256             }
7257             Roo.callback(o.success, o.scope, [this, action]);
7258             this.fireEvent('actioncomplete', this, action);
7259             
7260         }else{
7261             
7262             // failure condition..
7263             // we have a scenario where updates need confirming.
7264             // eg. if a locking scenario exists..
7265             // we look for { errors : { needs_confirm : true }} in the response.
7266             if (
7267                 (typeof(action.result) != 'undefined')  &&
7268                 (typeof(action.result.errors) != 'undefined')  &&
7269                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7270            ){
7271                 var _t = this;
7272                 Roo.log("not supported yet");
7273                  /*
7274                 
7275                 Roo.MessageBox.confirm(
7276                     "Change requires confirmation",
7277                     action.result.errorMsg,
7278                     function(r) {
7279                         if (r != 'yes') {
7280                             return;
7281                         }
7282                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7283                     }
7284                     
7285                 );
7286                 */
7287                 
7288                 
7289                 return;
7290             }
7291             
7292             Roo.callback(o.failure, o.scope, [this, action]);
7293             // show an error message if no failed handler is set..
7294             if (!this.hasListener('actionfailed')) {
7295                 Roo.log("need to add dialog support");
7296                 /*
7297                 Roo.MessageBox.alert("Error",
7298                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7299                         action.result.errorMsg :
7300                         "Saving Failed, please check your entries or try again"
7301                 );
7302                 */
7303             }
7304             
7305             this.fireEvent('actionfailed', this, action);
7306         }
7307         
7308     },
7309     /**
7310      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7311      * @param {String} id The value to search for
7312      * @return Field
7313      */
7314     findField : function(id){
7315         var items = this.getItems();
7316         var field = items.get(id);
7317         if(!field){
7318              items.each(function(f){
7319                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7320                     field = f;
7321                     return false;
7322                 }
7323                 return true;
7324             });
7325         }
7326         return field || null;
7327     },
7328      /**
7329      * Mark fields in this form invalid in bulk.
7330      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7331      * @return {BasicForm} this
7332      */
7333     markInvalid : function(errors){
7334         if(errors instanceof Array){
7335             for(var i = 0, len = errors.length; i < len; i++){
7336                 var fieldError = errors[i];
7337                 var f = this.findField(fieldError.id);
7338                 if(f){
7339                     f.markInvalid(fieldError.msg);
7340                 }
7341             }
7342         }else{
7343             var field, id;
7344             for(id in errors){
7345                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7346                     field.markInvalid(errors[id]);
7347                 }
7348             }
7349         }
7350         //Roo.each(this.childForms || [], function (f) {
7351         //    f.markInvalid(errors);
7352         //});
7353         
7354         return this;
7355     },
7356
7357     /**
7358      * Set values for fields in this form in bulk.
7359      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7360      * @return {BasicForm} this
7361      */
7362     setValues : function(values){
7363         if(values instanceof Array){ // array of objects
7364             for(var i = 0, len = values.length; i < len; i++){
7365                 var v = values[i];
7366                 var f = this.findField(v.id);
7367                 if(f){
7368                     f.setValue(v.value);
7369                     if(this.trackResetOnLoad){
7370                         f.originalValue = f.getValue();
7371                     }
7372                 }
7373             }
7374         }else{ // object hash
7375             var field, id;
7376             for(id in values){
7377                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7378                     
7379                     if (field.setFromData && 
7380                         field.valueField && 
7381                         field.displayField &&
7382                         // combos' with local stores can 
7383                         // be queried via setValue()
7384                         // to set their value..
7385                         (field.store && !field.store.isLocal)
7386                         ) {
7387                         // it's a combo
7388                         var sd = { };
7389                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7390                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7391                         field.setFromData(sd);
7392                         
7393                     } else {
7394                         field.setValue(values[id]);
7395                     }
7396                     
7397                     
7398                     if(this.trackResetOnLoad){
7399                         field.originalValue = field.getValue();
7400                     }
7401                 }
7402             }
7403         }
7404          
7405         //Roo.each(this.childForms || [], function (f) {
7406         //    f.setValues(values);
7407         //});
7408                 
7409         return this;
7410     },
7411
7412     /**
7413      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7414      * they are returned as an array.
7415      * @param {Boolean} asString
7416      * @return {Object}
7417      */
7418     getValues : function(asString){
7419         //if (this.childForms) {
7420             // copy values from the child forms
7421         //    Roo.each(this.childForms, function (f) {
7422         //        this.setValues(f.getValues());
7423         //    }, this);
7424         //}
7425         
7426         
7427         
7428         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7429         if(asString === true){
7430             return fs;
7431         }
7432         return Roo.urlDecode(fs);
7433     },
7434     
7435     /**
7436      * Returns the fields in this form as an object with key/value pairs. 
7437      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7438      * @return {Object}
7439      */
7440     getFieldValues : function(with_hidden)
7441     {
7442         var items = this.getItems();
7443         var ret = {};
7444         items.each(function(f){
7445             if (!f.getName()) {
7446                 return;
7447             }
7448             var v = f.getValue();
7449             if (f.inputType =='radio') {
7450                 if (typeof(ret[f.getName()]) == 'undefined') {
7451                     ret[f.getName()] = ''; // empty..
7452                 }
7453                 
7454                 if (!f.el.dom.checked) {
7455                     return;
7456                     
7457                 }
7458                 v = f.el.dom.value;
7459                 
7460             }
7461             
7462             // not sure if this supported any more..
7463             if ((typeof(v) == 'object') && f.getRawValue) {
7464                 v = f.getRawValue() ; // dates..
7465             }
7466             // combo boxes where name != hiddenName...
7467             if (f.name != f.getName()) {
7468                 ret[f.name] = f.getRawValue();
7469             }
7470             ret[f.getName()] = v;
7471         });
7472         
7473         return ret;
7474     },
7475
7476     /**
7477      * Clears all invalid messages in this form.
7478      * @return {BasicForm} this
7479      */
7480     clearInvalid : function(){
7481         var items = this.getItems();
7482         
7483         items.each(function(f){
7484            f.clearInvalid();
7485         });
7486         
7487         
7488         
7489         return this;
7490     },
7491
7492     /**
7493      * Resets this form.
7494      * @return {BasicForm} this
7495      */
7496     reset : function(){
7497         var items = this.getItems();
7498         items.each(function(f){
7499             f.reset();
7500         });
7501         
7502         Roo.each(this.childForms || [], function (f) {
7503             f.reset();
7504         });
7505        
7506         
7507         return this;
7508     },
7509     getItems : function()
7510     {
7511         var r=new Roo.util.MixedCollection(false, function(o){
7512             return o.id || (o.id = Roo.id());
7513         });
7514         var iter = function(el) {
7515             if (el.inputEl) {
7516                 r.add(el);
7517             }
7518             if (!el.items) {
7519                 return;
7520             }
7521             Roo.each(el.items,function(e) {
7522                 iter(e);
7523             });
7524             
7525             
7526         };
7527         
7528         iter(this);
7529         return r;
7530         
7531         
7532         
7533         
7534     }
7535     
7536 });
7537
7538  
7539 /*
7540  * Based on:
7541  * Ext JS Library 1.1.1
7542  * Copyright(c) 2006-2007, Ext JS, LLC.
7543  *
7544  * Originally Released Under LGPL - original licence link has changed is not relivant.
7545  *
7546  * Fork - LGPL
7547  * <script type="text/javascript">
7548  */
7549 /**
7550  * @class Roo.form.VTypes
7551  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7552  * @singleton
7553  */
7554 Roo.form.VTypes = function(){
7555     // closure these in so they are only created once.
7556     var alpha = /^[a-zA-Z_]+$/;
7557     var alphanum = /^[a-zA-Z0-9_]+$/;
7558     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7559     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7560
7561     // All these messages and functions are configurable
7562     return {
7563         /**
7564          * The function used to validate email addresses
7565          * @param {String} value The email address
7566          */
7567         'email' : function(v){
7568             return email.test(v);
7569         },
7570         /**
7571          * The error text to display when the email validation function returns false
7572          * @type String
7573          */
7574         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7575         /**
7576          * The keystroke filter mask to be applied on email input
7577          * @type RegExp
7578          */
7579         'emailMask' : /[a-z0-9_\.\-@]/i,
7580
7581         /**
7582          * The function used to validate URLs
7583          * @param {String} value The URL
7584          */
7585         'url' : function(v){
7586             return url.test(v);
7587         },
7588         /**
7589          * The error text to display when the url validation function returns false
7590          * @type String
7591          */
7592         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7593         
7594         /**
7595          * The function used to validate alpha values
7596          * @param {String} value The value
7597          */
7598         'alpha' : function(v){
7599             return alpha.test(v);
7600         },
7601         /**
7602          * The error text to display when the alpha validation function returns false
7603          * @type String
7604          */
7605         'alphaText' : 'This field should only contain letters and _',
7606         /**
7607          * The keystroke filter mask to be applied on alpha input
7608          * @type RegExp
7609          */
7610         'alphaMask' : /[a-z_]/i,
7611
7612         /**
7613          * The function used to validate alphanumeric values
7614          * @param {String} value The value
7615          */
7616         'alphanum' : function(v){
7617             return alphanum.test(v);
7618         },
7619         /**
7620          * The error text to display when the alphanumeric validation function returns false
7621          * @type String
7622          */
7623         'alphanumText' : 'This field should only contain letters, numbers and _',
7624         /**
7625          * The keystroke filter mask to be applied on alphanumeric input
7626          * @type RegExp
7627          */
7628         'alphanumMask' : /[a-z0-9_]/i
7629     };
7630 }();/*
7631  * - LGPL
7632  *
7633  * Input
7634  * 
7635  */
7636
7637 /**
7638  * @class Roo.bootstrap.Input
7639  * @extends Roo.bootstrap.Component
7640  * Bootstrap Input class
7641  * @cfg {Boolean} disabled is it disabled
7642  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7643  * @cfg {String} name name of the input
7644  * @cfg {string} fieldLabel - the label associated
7645  * @cfg {string} placeholder - placeholder to put in text.
7646  * @cfg {string}  before - input group add on before
7647  * @cfg {string} after - input group add on after
7648  * @cfg {string} size - (lg|sm) or leave empty..
7649  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7650  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7651  * @cfg {Number} md colspan out of 12 for computer-sized screens
7652  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7653  * @cfg {string} value default value of the input
7654  * @cfg {Number} labelWidth set the width of label (0-12)
7655  * @cfg {String} labelAlign (top|left)
7656  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7657  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7658
7659  * @cfg {String} align (left|center|right) Default left
7660  * @cfg {Boolean} forceFeedback (true|false) Default false
7661  * 
7662  * 
7663  * 
7664  * 
7665  * @constructor
7666  * Create a new Input
7667  * @param {Object} config The config object
7668  */
7669
7670 Roo.bootstrap.Input = function(config){
7671     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7672    
7673         this.addEvents({
7674             /**
7675              * @event focus
7676              * Fires when this field receives input focus.
7677              * @param {Roo.form.Field} this
7678              */
7679             focus : true,
7680             /**
7681              * @event blur
7682              * Fires when this field loses input focus.
7683              * @param {Roo.form.Field} this
7684              */
7685             blur : true,
7686             /**
7687              * @event specialkey
7688              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7689              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7690              * @param {Roo.form.Field} this
7691              * @param {Roo.EventObject} e The event object
7692              */
7693             specialkey : true,
7694             /**
7695              * @event change
7696              * Fires just before the field blurs if the field value has changed.
7697              * @param {Roo.form.Field} this
7698              * @param {Mixed} newValue The new value
7699              * @param {Mixed} oldValue The original value
7700              */
7701             change : true,
7702             /**
7703              * @event invalid
7704              * Fires after the field has been marked as invalid.
7705              * @param {Roo.form.Field} this
7706              * @param {String} msg The validation message
7707              */
7708             invalid : true,
7709             /**
7710              * @event valid
7711              * Fires after the field has been validated with no errors.
7712              * @param {Roo.form.Field} this
7713              */
7714             valid : true,
7715              /**
7716              * @event keyup
7717              * Fires after the key up
7718              * @param {Roo.form.Field} this
7719              * @param {Roo.EventObject}  e The event Object
7720              */
7721             keyup : true
7722         });
7723 };
7724
7725 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7726      /**
7727      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7728       automatic validation (defaults to "keyup").
7729      */
7730     validationEvent : "keyup",
7731      /**
7732      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7733      */
7734     validateOnBlur : true,
7735     /**
7736      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7737      */
7738     validationDelay : 250,
7739      /**
7740      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7741      */
7742     focusClass : "x-form-focus",  // not needed???
7743     
7744        
7745     /**
7746      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7747      */
7748     invalidClass : "has-warning",
7749     
7750     /**
7751      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7752      */
7753     validClass : "has-success",
7754     
7755     /**
7756      * @cfg {Boolean} hasFeedback (true|false) default true
7757      */
7758     hasFeedback : true,
7759     
7760     /**
7761      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7762      */
7763     invalidFeedbackClass : "glyphicon-warning-sign",
7764     
7765     /**
7766      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7767      */
7768     validFeedbackClass : "glyphicon-ok",
7769     
7770     /**
7771      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7772      */
7773     selectOnFocus : false,
7774     
7775      /**
7776      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7777      */
7778     maskRe : null,
7779        /**
7780      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7781      */
7782     vtype : null,
7783     
7784       /**
7785      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7786      */
7787     disableKeyFilter : false,
7788     
7789        /**
7790      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7791      */
7792     disabled : false,
7793      /**
7794      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7795      */
7796     allowBlank : true,
7797     /**
7798      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7799      */
7800     blankText : "This field is required",
7801     
7802      /**
7803      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7804      */
7805     minLength : 0,
7806     /**
7807      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7808      */
7809     maxLength : Number.MAX_VALUE,
7810     /**
7811      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7812      */
7813     minLengthText : "The minimum length for this field is {0}",
7814     /**
7815      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7816      */
7817     maxLengthText : "The maximum length for this field is {0}",
7818   
7819     
7820     /**
7821      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7822      * If available, this function will be called only after the basic validators all return true, and will be passed the
7823      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7824      */
7825     validator : null,
7826     /**
7827      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7828      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7829      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7830      */
7831     regex : null,
7832     /**
7833      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7834      */
7835     regexText : "",
7836     
7837     autocomplete: false,
7838     
7839     
7840     fieldLabel : '',
7841     inputType : 'text',
7842     
7843     name : false,
7844     placeholder: false,
7845     before : false,
7846     after : false,
7847     size : false,
7848     hasFocus : false,
7849     preventMark: false,
7850     isFormField : true,
7851     value : '',
7852     labelWidth : 2,
7853     labelAlign : false,
7854     readOnly : false,
7855     align : false,
7856     formatedValue : false,
7857     forceFeedback : false,
7858     
7859     parentLabelAlign : function()
7860     {
7861         var parent = this;
7862         while (parent.parent()) {
7863             parent = parent.parent();
7864             if (typeof(parent.labelAlign) !='undefined') {
7865                 return parent.labelAlign;
7866             }
7867         }
7868         return 'left';
7869         
7870     },
7871     
7872     getAutoCreate : function(){
7873         
7874         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7875         
7876         var id = Roo.id();
7877         
7878         var cfg = {};
7879         
7880         if(this.inputType != 'hidden'){
7881             cfg.cls = 'form-group' //input-group
7882         }
7883         
7884         var input =  {
7885             tag: 'input',
7886             id : id,
7887             type : this.inputType,
7888             value : this.value,
7889             cls : 'form-control',
7890             placeholder : this.placeholder || '',
7891             autocomplete : this.autocomplete || 'new-password'
7892         };
7893         
7894         
7895         if(this.align){
7896             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7897         }
7898         
7899         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7900             input.maxLength = this.maxLength;
7901         }
7902         
7903         if (this.disabled) {
7904             input.disabled=true;
7905         }
7906         
7907         if (this.readOnly) {
7908             input.readonly=true;
7909         }
7910         
7911         if (this.name) {
7912             input.name = this.name;
7913         }
7914         if (this.size) {
7915             input.cls += ' input-' + this.size;
7916         }
7917         var settings=this;
7918         ['xs','sm','md','lg'].map(function(size){
7919             if (settings[size]) {
7920                 cfg.cls += ' col-' + size + '-' + settings[size];
7921             }
7922         });
7923         
7924         var inputblock = input;
7925         
7926         var feedback = {
7927             tag: 'span',
7928             cls: 'glyphicon form-control-feedback'
7929         };
7930             
7931         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7932             
7933             inputblock = {
7934                 cls : 'has-feedback',
7935                 cn :  [
7936                     input,
7937                     feedback
7938                 ] 
7939             };  
7940         }
7941         
7942         if (this.before || this.after) {
7943             
7944             inputblock = {
7945                 cls : 'input-group',
7946                 cn :  [] 
7947             };
7948             
7949             if (this.before && typeof(this.before) == 'string') {
7950                 
7951                 inputblock.cn.push({
7952                     tag :'span',
7953                     cls : 'roo-input-before input-group-addon',
7954                     html : this.before
7955                 });
7956             }
7957             if (this.before && typeof(this.before) == 'object') {
7958                 this.before = Roo.factory(this.before);
7959                 Roo.log(this.before);
7960                 inputblock.cn.push({
7961                     tag :'span',
7962                     cls : 'roo-input-before input-group-' +
7963                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7964                 });
7965             }
7966             
7967             inputblock.cn.push(input);
7968             
7969             if (this.after && typeof(this.after) == 'string') {
7970                 inputblock.cn.push({
7971                     tag :'span',
7972                     cls : 'roo-input-after input-group-addon',
7973                     html : this.after
7974                 });
7975             }
7976             if (this.after && typeof(this.after) == 'object') {
7977                 this.after = Roo.factory(this.after);
7978                 Roo.log(this.after);
7979                 inputblock.cn.push({
7980                     tag :'span',
7981                     cls : 'roo-input-after input-group-' +
7982                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7983                 });
7984             }
7985             
7986             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7987                 inputblock.cls += ' has-feedback';
7988                 inputblock.cn.push(feedback);
7989             }
7990         };
7991         
7992         if (align ==='left' && this.fieldLabel.length) {
7993                 Roo.log("left and has label");
7994                 cfg.cn = [
7995                     
7996                     {
7997                         tag: 'label',
7998                         'for' :  id,
7999                         cls : 'control-label col-sm-' + this.labelWidth,
8000                         html : this.fieldLabel
8001                         
8002                     },
8003                     {
8004                         cls : "col-sm-" + (12 - this.labelWidth), 
8005                         cn: [
8006                             inputblock
8007                         ]
8008                     }
8009                     
8010                 ];
8011         } else if ( this.fieldLabel.length) {
8012                 Roo.log(" label");
8013                  cfg.cn = [
8014                    
8015                     {
8016                         tag: 'label',
8017                         //cls : 'input-group-addon',
8018                         html : this.fieldLabel
8019                         
8020                     },
8021                     
8022                     inputblock
8023                     
8024                 ];
8025
8026         } else {
8027             
8028                 Roo.log(" no label && no align");
8029                 cfg.cn = [
8030                     
8031                         inputblock
8032                     
8033                 ];
8034                 
8035                 
8036         };
8037         Roo.log('input-parentType: ' + this.parentType);
8038         
8039         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8040            cfg.cls += ' navbar-form';
8041            Roo.log(cfg);
8042         }
8043         
8044         return cfg;
8045         
8046     },
8047     /**
8048      * return the real input element.
8049      */
8050     inputEl: function ()
8051     {
8052         return this.el.select('input.form-control',true).first();
8053     },
8054     
8055     tooltipEl : function()
8056     {
8057         return this.inputEl();
8058     },
8059     
8060     setDisabled : function(v)
8061     {
8062         var i  = this.inputEl().dom;
8063         if (!v) {
8064             i.removeAttribute('disabled');
8065             return;
8066             
8067         }
8068         i.setAttribute('disabled','true');
8069     },
8070     initEvents : function()
8071     {
8072           
8073         this.inputEl().on("keydown" , this.fireKey,  this);
8074         this.inputEl().on("focus", this.onFocus,  this);
8075         this.inputEl().on("blur", this.onBlur,  this);
8076         
8077         this.inputEl().relayEvent('keyup', this);
8078  
8079         // reference to original value for reset
8080         this.originalValue = this.getValue();
8081         //Roo.form.TextField.superclass.initEvents.call(this);
8082         if(this.validationEvent == 'keyup'){
8083             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8084             this.inputEl().on('keyup', this.filterValidation, this);
8085         }
8086         else if(this.validationEvent !== false){
8087             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8088         }
8089         
8090         if(this.selectOnFocus){
8091             this.on("focus", this.preFocus, this);
8092             
8093         }
8094         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8095             this.inputEl().on("keypress", this.filterKeys, this);
8096         }
8097        /* if(this.grow){
8098             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8099             this.el.on("click", this.autoSize,  this);
8100         }
8101         */
8102         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8103             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8104         }
8105         
8106         if (typeof(this.before) == 'object') {
8107             this.before.render(this.el.select('.roo-input-before',true).first());
8108         }
8109         if (typeof(this.after) == 'object') {
8110             this.after.render(this.el.select('.roo-input-after',true).first());
8111         }
8112         
8113         
8114     },
8115     filterValidation : function(e){
8116         if(!e.isNavKeyPress()){
8117             this.validationTask.delay(this.validationDelay);
8118         }
8119     },
8120      /**
8121      * Validates the field value
8122      * @return {Boolean} True if the value is valid, else false
8123      */
8124     validate : function(){
8125         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8126         if(this.disabled || this.validateValue(this.getRawValue())){
8127             this.markValid();
8128             return true;
8129         }
8130         
8131         this.markInvalid();
8132         return false;
8133     },
8134     
8135     
8136     /**
8137      * Validates a value according to the field's validation rules and marks the field as invalid
8138      * if the validation fails
8139      * @param {Mixed} value The value to validate
8140      * @return {Boolean} True if the value is valid, else false
8141      */
8142     validateValue : function(value){
8143         if(value.length < 1)  { // if it's blank
8144             if(this.allowBlank){
8145                 return true;
8146             }
8147             return false;
8148         }
8149         
8150         if(value.length < this.minLength){
8151             return false;
8152         }
8153         if(value.length > this.maxLength){
8154             return false;
8155         }
8156         if(this.vtype){
8157             var vt = Roo.form.VTypes;
8158             if(!vt[this.vtype](value, this)){
8159                 return false;
8160             }
8161         }
8162         if(typeof this.validator == "function"){
8163             var msg = this.validator(value);
8164             if(msg !== true){
8165                 return false;
8166             }
8167         }
8168         
8169         if(this.regex && !this.regex.test(value)){
8170             return false;
8171         }
8172         
8173         return true;
8174     },
8175
8176     
8177     
8178      // private
8179     fireKey : function(e){
8180         //Roo.log('field ' + e.getKey());
8181         if(e.isNavKeyPress()){
8182             this.fireEvent("specialkey", this, e);
8183         }
8184     },
8185     focus : function (selectText){
8186         if(this.rendered){
8187             this.inputEl().focus();
8188             if(selectText === true){
8189                 this.inputEl().dom.select();
8190             }
8191         }
8192         return this;
8193     } ,
8194     
8195     onFocus : function(){
8196         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8197            // this.el.addClass(this.focusClass);
8198         }
8199         if(!this.hasFocus){
8200             this.hasFocus = true;
8201             this.startValue = this.getValue();
8202             this.fireEvent("focus", this);
8203         }
8204     },
8205     
8206     beforeBlur : Roo.emptyFn,
8207
8208     
8209     // private
8210     onBlur : function(){
8211         this.beforeBlur();
8212         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8213             //this.el.removeClass(this.focusClass);
8214         }
8215         this.hasFocus = false;
8216         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8217             this.validate();
8218         }
8219         var v = this.getValue();
8220         if(String(v) !== String(this.startValue)){
8221             this.fireEvent('change', this, v, this.startValue);
8222         }
8223         this.fireEvent("blur", this);
8224     },
8225     
8226     /**
8227      * Resets the current field value to the originally loaded value and clears any validation messages
8228      */
8229     reset : function(){
8230         this.setValue(this.originalValue);
8231         this.validate();
8232     },
8233      /**
8234      * Returns the name of the field
8235      * @return {Mixed} name The name field
8236      */
8237     getName: function(){
8238         return this.name;
8239     },
8240      /**
8241      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8242      * @return {Mixed} value The field value
8243      */
8244     getValue : function(){
8245         
8246         var v = this.inputEl().getValue();
8247         
8248         return v;
8249     },
8250     /**
8251      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8252      * @return {Mixed} value The field value
8253      */
8254     getRawValue : function(){
8255         var v = this.inputEl().getValue();
8256         
8257         return v;
8258     },
8259     
8260     /**
8261      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8262      * @param {Mixed} value The value to set
8263      */
8264     setRawValue : function(v){
8265         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8266     },
8267     
8268     selectText : function(start, end){
8269         var v = this.getRawValue();
8270         if(v.length > 0){
8271             start = start === undefined ? 0 : start;
8272             end = end === undefined ? v.length : end;
8273             var d = this.inputEl().dom;
8274             if(d.setSelectionRange){
8275                 d.setSelectionRange(start, end);
8276             }else if(d.createTextRange){
8277                 var range = d.createTextRange();
8278                 range.moveStart("character", start);
8279                 range.moveEnd("character", v.length-end);
8280                 range.select();
8281             }
8282         }
8283     },
8284     
8285     /**
8286      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8287      * @param {Mixed} value The value to set
8288      */
8289     setValue : function(v){
8290         this.value = v;
8291         if(this.rendered){
8292             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8293             this.validate();
8294         }
8295     },
8296     
8297     /*
8298     processValue : function(value){
8299         if(this.stripCharsRe){
8300             var newValue = value.replace(this.stripCharsRe, '');
8301             if(newValue !== value){
8302                 this.setRawValue(newValue);
8303                 return newValue;
8304             }
8305         }
8306         return value;
8307     },
8308   */
8309     preFocus : function(){
8310         
8311         if(this.selectOnFocus){
8312             this.inputEl().dom.select();
8313         }
8314     },
8315     filterKeys : function(e){
8316         var k = e.getKey();
8317         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8318             return;
8319         }
8320         var c = e.getCharCode(), cc = String.fromCharCode(c);
8321         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8322             return;
8323         }
8324         if(!this.maskRe.test(cc)){
8325             e.stopEvent();
8326         }
8327     },
8328      /**
8329      * Clear any invalid styles/messages for this field
8330      */
8331     clearInvalid : function(){
8332         
8333         if(!this.el || this.preventMark){ // not rendered
8334             return;
8335         }
8336         this.el.removeClass(this.invalidClass);
8337         
8338         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8339             
8340             var feedback = this.el.select('.form-control-feedback', true).first();
8341             
8342             if(feedback){
8343                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8344             }
8345             
8346         }
8347         
8348         this.fireEvent('valid', this);
8349     },
8350     
8351      /**
8352      * Mark this field as valid
8353      */
8354     markValid : function()
8355     {
8356         if(!this.el  || this.preventMark){ // not rendered
8357             return;
8358         }
8359         
8360         this.el.removeClass([this.invalidClass, this.validClass]);
8361         
8362         var feedback = this.el.select('.form-control-feedback', true).first();
8363             
8364         if(feedback){
8365             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8366         }
8367
8368         if(this.disabled || this.allowBlank){
8369             return;
8370         }
8371         
8372         var formGroup = this.el.findParent('.form-group', false, true);
8373         
8374         if(formGroup){
8375             
8376             var label = formGroup.select('label', true).first();
8377             var icon = formGroup.select('i.fa-star', true).first();
8378             
8379             if(label && icon){
8380                 icon.remove();
8381             }
8382         }
8383         
8384         this.el.addClass(this.validClass);
8385         
8386         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8387             
8388             var feedback = this.el.select('.form-control-feedback', true).first();
8389             
8390             if(feedback){
8391                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8392                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8393             }
8394             
8395         }
8396         
8397         this.fireEvent('valid', this);
8398     },
8399     
8400      /**
8401      * Mark this field as invalid
8402      * @param {String} msg The validation message
8403      */
8404     markInvalid : function(msg)
8405     {
8406         if(!this.el  || this.preventMark){ // not rendered
8407             return;
8408         }
8409         
8410         this.el.removeClass([this.invalidClass, this.validClass]);
8411         
8412         var feedback = this.el.select('.form-control-feedback', true).first();
8413             
8414         if(feedback){
8415             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8416         }
8417
8418         if(this.disabled || this.allowBlank){
8419             return;
8420         }
8421         
8422         var formGroup = this.el.findParent('.form-group', false, true);
8423         
8424         if(formGroup){
8425             var label = formGroup.select('label', true).first();
8426             var icon = formGroup.select('i.fa-star', true).first();
8427
8428             if(!this.getValue().length && label && !icon){
8429                 this.el.findParent('.form-group', false, true).createChild({
8430                     tag : 'i',
8431                     cls : 'text-danger fa fa-lg fa-star',
8432                     tooltip : 'This field is required',
8433                     style : 'margin-right:5px;'
8434                 }, label, true);
8435             }
8436         }
8437         
8438         
8439         this.el.addClass(this.invalidClass);
8440         
8441         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8442             
8443             var feedback = this.el.select('.form-control-feedback', true).first();
8444             
8445             if(feedback){
8446                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8447                 
8448                 if(this.getValue().length || this.forceFeedback){
8449                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8450                 }
8451                 
8452             }
8453             
8454         }
8455         
8456         this.fireEvent('invalid', this, msg);
8457     },
8458     // private
8459     SafariOnKeyDown : function(event)
8460     {
8461         // this is a workaround for a password hang bug on chrome/ webkit.
8462         
8463         var isSelectAll = false;
8464         
8465         if(this.inputEl().dom.selectionEnd > 0){
8466             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8467         }
8468         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8469             event.preventDefault();
8470             this.setValue('');
8471             return;
8472         }
8473         
8474         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8475             
8476             event.preventDefault();
8477             // this is very hacky as keydown always get's upper case.
8478             //
8479             var cc = String.fromCharCode(event.getCharCode());
8480             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8481             
8482         }
8483     },
8484     adjustWidth : function(tag, w){
8485         tag = tag.toLowerCase();
8486         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8487             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8488                 if(tag == 'input'){
8489                     return w + 2;
8490                 }
8491                 if(tag == 'textarea'){
8492                     return w-2;
8493                 }
8494             }else if(Roo.isOpera){
8495                 if(tag == 'input'){
8496                     return w + 2;
8497                 }
8498                 if(tag == 'textarea'){
8499                     return w-2;
8500                 }
8501             }
8502         }
8503         return w;
8504     }
8505     
8506 });
8507
8508  
8509 /*
8510  * - LGPL
8511  *
8512  * Input
8513  * 
8514  */
8515
8516 /**
8517  * @class Roo.bootstrap.TextArea
8518  * @extends Roo.bootstrap.Input
8519  * Bootstrap TextArea class
8520  * @cfg {Number} cols Specifies the visible width of a text area
8521  * @cfg {Number} rows Specifies the visible number of lines in a text area
8522  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8523  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8524  * @cfg {string} html text
8525  * 
8526  * @constructor
8527  * Create a new TextArea
8528  * @param {Object} config The config object
8529  */
8530
8531 Roo.bootstrap.TextArea = function(config){
8532     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8533    
8534 };
8535
8536 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8537      
8538     cols : false,
8539     rows : 5,
8540     readOnly : false,
8541     warp : 'soft',
8542     resize : false,
8543     value: false,
8544     html: false,
8545     
8546     getAutoCreate : function(){
8547         
8548         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8549         
8550         var id = Roo.id();
8551         
8552         var cfg = {};
8553         
8554         var input =  {
8555             tag: 'textarea',
8556             id : id,
8557             warp : this.warp,
8558             rows : this.rows,
8559             value : this.value || '',
8560             html: this.html || '',
8561             cls : 'form-control',
8562             placeholder : this.placeholder || '' 
8563             
8564         };
8565         
8566         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8567             input.maxLength = this.maxLength;
8568         }
8569         
8570         if(this.resize){
8571             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8572         }
8573         
8574         if(this.cols){
8575             input.cols = this.cols;
8576         }
8577         
8578         if (this.readOnly) {
8579             input.readonly = true;
8580         }
8581         
8582         if (this.name) {
8583             input.name = this.name;
8584         }
8585         
8586         if (this.size) {
8587             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8588         }
8589         
8590         var settings=this;
8591         ['xs','sm','md','lg'].map(function(size){
8592             if (settings[size]) {
8593                 cfg.cls += ' col-' + size + '-' + settings[size];
8594             }
8595         });
8596         
8597         var inputblock = input;
8598         
8599         if(this.hasFeedback && !this.allowBlank){
8600             
8601             var feedback = {
8602                 tag: 'span',
8603                 cls: 'glyphicon form-control-feedback'
8604             };
8605
8606             inputblock = {
8607                 cls : 'has-feedback',
8608                 cn :  [
8609                     input,
8610                     feedback
8611                 ] 
8612             };  
8613         }
8614         
8615         
8616         if (this.before || this.after) {
8617             
8618             inputblock = {
8619                 cls : 'input-group',
8620                 cn :  [] 
8621             };
8622             if (this.before) {
8623                 inputblock.cn.push({
8624                     tag :'span',
8625                     cls : 'input-group-addon',
8626                     html : this.before
8627                 });
8628             }
8629             
8630             inputblock.cn.push(input);
8631             
8632             if(this.hasFeedback && !this.allowBlank){
8633                 inputblock.cls += ' has-feedback';
8634                 inputblock.cn.push(feedback);
8635             }
8636             
8637             if (this.after) {
8638                 inputblock.cn.push({
8639                     tag :'span',
8640                     cls : 'input-group-addon',
8641                     html : this.after
8642                 });
8643             }
8644             
8645         }
8646         
8647         if (align ==='left' && this.fieldLabel.length) {
8648                 Roo.log("left and has label");
8649                 cfg.cn = [
8650                     
8651                     {
8652                         tag: 'label',
8653                         'for' :  id,
8654                         cls : 'control-label col-sm-' + this.labelWidth,
8655                         html : this.fieldLabel
8656                         
8657                     },
8658                     {
8659                         cls : "col-sm-" + (12 - this.labelWidth), 
8660                         cn: [
8661                             inputblock
8662                         ]
8663                     }
8664                     
8665                 ];
8666         } else if ( this.fieldLabel.length) {
8667                 Roo.log(" label");
8668                  cfg.cn = [
8669                    
8670                     {
8671                         tag: 'label',
8672                         //cls : 'input-group-addon',
8673                         html : this.fieldLabel
8674                         
8675                     },
8676                     
8677                     inputblock
8678                     
8679                 ];
8680
8681         } else {
8682             
8683                    Roo.log(" no label && no align");
8684                 cfg.cn = [
8685                     
8686                         inputblock
8687                     
8688                 ];
8689                 
8690                 
8691         }
8692         
8693         if (this.disabled) {
8694             input.disabled=true;
8695         }
8696         
8697         return cfg;
8698         
8699     },
8700     /**
8701      * return the real textarea element.
8702      */
8703     inputEl: function ()
8704     {
8705         return this.el.select('textarea.form-control',true).first();
8706     }
8707 });
8708
8709  
8710 /*
8711  * - LGPL
8712  *
8713  * trigger field - base class for combo..
8714  * 
8715  */
8716  
8717 /**
8718  * @class Roo.bootstrap.TriggerField
8719  * @extends Roo.bootstrap.Input
8720  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8721  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8722  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8723  * for which you can provide a custom implementation.  For example:
8724  * <pre><code>
8725 var trigger = new Roo.bootstrap.TriggerField();
8726 trigger.onTriggerClick = myTriggerFn;
8727 trigger.applyTo('my-field');
8728 </code></pre>
8729  *
8730  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8731  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8732  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8733  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8734  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8735
8736  * @constructor
8737  * Create a new TriggerField.
8738  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8739  * to the base TextField)
8740  */
8741 Roo.bootstrap.TriggerField = function(config){
8742     this.mimicing = false;
8743     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8744 };
8745
8746 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8747     /**
8748      * @cfg {String} triggerClass A CSS class to apply to the trigger
8749      */
8750      /**
8751      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8752      */
8753     hideTrigger:false,
8754
8755     /**
8756      * @cfg {Boolean} removable (true|false) special filter default false
8757      */
8758     removable : false,
8759     
8760     /** @cfg {Boolean} grow @hide */
8761     /** @cfg {Number} growMin @hide */
8762     /** @cfg {Number} growMax @hide */
8763
8764     /**
8765      * @hide 
8766      * @method
8767      */
8768     autoSize: Roo.emptyFn,
8769     // private
8770     monitorTab : true,
8771     // private
8772     deferHeight : true,
8773
8774     
8775     actionMode : 'wrap',
8776     
8777     caret : false,
8778     
8779     
8780     getAutoCreate : function(){
8781        
8782         var align = this.labelAlign || this.parentLabelAlign();
8783         
8784         var id = Roo.id();
8785         
8786         var cfg = {
8787             cls: 'form-group' //input-group
8788         };
8789         
8790         
8791         var input =  {
8792             tag: 'input',
8793             id : id,
8794             type : this.inputType,
8795             cls : 'form-control',
8796             autocomplete: 'new-password',
8797             placeholder : this.placeholder || '' 
8798             
8799         };
8800         if (this.name) {
8801             input.name = this.name;
8802         }
8803         if (this.size) {
8804             input.cls += ' input-' + this.size;
8805         }
8806         
8807         if (this.disabled) {
8808             input.disabled=true;
8809         }
8810         
8811         var inputblock = input;
8812         
8813         if(this.hasFeedback && !this.allowBlank){
8814             
8815             var feedback = {
8816                 tag: 'span',
8817                 cls: 'glyphicon form-control-feedback'
8818             };
8819             
8820             if(this.removable && !this.editable && !this.tickable){
8821                 inputblock = {
8822                     cls : 'has-feedback',
8823                     cn :  [
8824                         inputblock,
8825                         {
8826                             tag: 'button',
8827                             html : 'x',
8828                             cls : 'roo-combo-removable-btn close'
8829                         },
8830                         feedback
8831                     ] 
8832                 };
8833             } else {
8834                 inputblock = {
8835                     cls : 'has-feedback',
8836                     cn :  [
8837                         inputblock,
8838                         feedback
8839                     ] 
8840                 };
8841             }
8842
8843         } else {
8844             if(this.removable && !this.editable && !this.tickable){
8845                 inputblock = {
8846                     cls : 'roo-removable',
8847                     cn :  [
8848                         inputblock,
8849                         {
8850                             tag: 'button',
8851                             html : 'x',
8852                             cls : 'roo-combo-removable-btn close'
8853                         }
8854                     ] 
8855                 };
8856             }
8857         }
8858         
8859         if (this.before || this.after) {
8860             
8861             inputblock = {
8862                 cls : 'input-group',
8863                 cn :  [] 
8864             };
8865             if (this.before) {
8866                 inputblock.cn.push({
8867                     tag :'span',
8868                     cls : 'input-group-addon',
8869                     html : this.before
8870                 });
8871             }
8872             
8873             inputblock.cn.push(input);
8874             
8875             if(this.hasFeedback && !this.allowBlank){
8876                 inputblock.cls += ' has-feedback';
8877                 inputblock.cn.push(feedback);
8878             }
8879             
8880             if (this.after) {
8881                 inputblock.cn.push({
8882                     tag :'span',
8883                     cls : 'input-group-addon',
8884                     html : this.after
8885                 });
8886             }
8887             
8888         };
8889         
8890         var box = {
8891             tag: 'div',
8892             cn: [
8893                 {
8894                     tag: 'input',
8895                     type : 'hidden',
8896                     cls: 'form-hidden-field'
8897                 },
8898                 inputblock
8899             ]
8900             
8901         };
8902         
8903         if(this.multiple){
8904             Roo.log('multiple');
8905             
8906             box = {
8907                 tag: 'div',
8908                 cn: [
8909                     {
8910                         tag: 'input',
8911                         type : 'hidden',
8912                         cls: 'form-hidden-field'
8913                     },
8914                     {
8915                         tag: 'ul',
8916                         cls: 'select2-choices',
8917                         cn:[
8918                             {
8919                                 tag: 'li',
8920                                 cls: 'select2-search-field',
8921                                 cn: [
8922
8923                                     inputblock
8924                                 ]
8925                             }
8926                         ]
8927                     }
8928                 ]
8929             }
8930         };
8931         
8932         var combobox = {
8933             cls: 'select2-container input-group',
8934             cn: [
8935                 box
8936 //                {
8937 //                    tag: 'ul',
8938 //                    cls: 'typeahead typeahead-long dropdown-menu',
8939 //                    style: 'display:none'
8940 //                }
8941             ]
8942         };
8943         
8944         if(!this.multiple && this.showToggleBtn){
8945             
8946             var caret = {
8947                         tag: 'span',
8948                         cls: 'caret'
8949              };
8950             if (this.caret != false) {
8951                 caret = {
8952                      tag: 'i',
8953                      cls: 'fa fa-' + this.caret
8954                 };
8955                 
8956             }
8957             
8958             combobox.cn.push({
8959                 tag :'span',
8960                 cls : 'input-group-addon btn dropdown-toggle',
8961                 cn : [
8962                     caret,
8963                     {
8964                         tag: 'span',
8965                         cls: 'combobox-clear',
8966                         cn  : [
8967                             {
8968                                 tag : 'i',
8969                                 cls: 'icon-remove'
8970                             }
8971                         ]
8972                     }
8973                 ]
8974
8975             })
8976         }
8977         
8978         if(this.multiple){
8979             combobox.cls += ' select2-container-multi';
8980         }
8981         
8982         if (align ==='left' && this.fieldLabel.length) {
8983             
8984                 Roo.log("left and has label");
8985                 cfg.cn = [
8986                     
8987                     {
8988                         tag: 'label',
8989                         'for' :  id,
8990                         cls : 'control-label col-sm-' + this.labelWidth,
8991                         html : this.fieldLabel
8992                         
8993                     },
8994                     {
8995                         cls : "col-sm-" + (12 - this.labelWidth), 
8996                         cn: [
8997                             combobox
8998                         ]
8999                     }
9000                     
9001                 ];
9002         } else if ( this.fieldLabel.length) {
9003                 Roo.log(" label");
9004                  cfg.cn = [
9005                    
9006                     {
9007                         tag: 'label',
9008                         //cls : 'input-group-addon',
9009                         html : this.fieldLabel
9010                         
9011                     },
9012                     
9013                     combobox
9014                     
9015                 ];
9016
9017         } else {
9018             
9019                 Roo.log(" no label && no align");
9020                 cfg = combobox
9021                      
9022                 
9023         }
9024          
9025         var settings=this;
9026         ['xs','sm','md','lg'].map(function(size){
9027             if (settings[size]) {
9028                 cfg.cls += ' col-' + size + '-' + settings[size];
9029             }
9030         });
9031         Roo.log(cfg);
9032         return cfg;
9033         
9034     },
9035     
9036     
9037     
9038     // private
9039     onResize : function(w, h){
9040 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9041 //        if(typeof w == 'number'){
9042 //            var x = w - this.trigger.getWidth();
9043 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9044 //            this.trigger.setStyle('left', x+'px');
9045 //        }
9046     },
9047
9048     // private
9049     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9050
9051     // private
9052     getResizeEl : function(){
9053         return this.inputEl();
9054     },
9055
9056     // private
9057     getPositionEl : function(){
9058         return this.inputEl();
9059     },
9060
9061     // private
9062     alignErrorIcon : function(){
9063         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9064     },
9065
9066     // private
9067     initEvents : function(){
9068         
9069         this.createList();
9070         
9071         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9072         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9073         if(!this.multiple && this.showToggleBtn){
9074             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9075             if(this.hideTrigger){
9076                 this.trigger.setDisplayed(false);
9077             }
9078             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9079         }
9080         
9081         if(this.multiple){
9082             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9083         }
9084         
9085         if(this.removable && !this.editable && !this.tickable){
9086             var close = this.closeTriggerEl();
9087             
9088             if(close){
9089                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9090                 close.on('click', this.removeBtnClick, this, close);
9091             }
9092         }
9093         
9094         //this.trigger.addClassOnOver('x-form-trigger-over');
9095         //this.trigger.addClassOnClick('x-form-trigger-click');
9096         
9097         //if(!this.width){
9098         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9099         //}
9100     },
9101     
9102     closeTriggerEl : function()
9103     {
9104         var close = this.el.select('.roo-combo-removable-btn', true).first();
9105         return close ? close : false;
9106     },
9107     
9108     removeBtnClick : function(e, h, el)
9109     {
9110         e.preventDefault();
9111         
9112         if(this.fireEvent("remove", this) !== false){
9113             this.reset();
9114         }
9115     },
9116     
9117     createList : function()
9118     {
9119         this.list = Roo.get(document.body).createChild({
9120             tag: 'ul',
9121             cls: 'typeahead typeahead-long dropdown-menu',
9122             style: 'display:none'
9123         });
9124         
9125         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9126         
9127     },
9128
9129     // private
9130     initTrigger : function(){
9131        
9132     },
9133
9134     // private
9135     onDestroy : function(){
9136         if(this.trigger){
9137             this.trigger.removeAllListeners();
9138           //  this.trigger.remove();
9139         }
9140         //if(this.wrap){
9141         //    this.wrap.remove();
9142         //}
9143         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9144     },
9145
9146     // private
9147     onFocus : function(){
9148         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9149         /*
9150         if(!this.mimicing){
9151             this.wrap.addClass('x-trigger-wrap-focus');
9152             this.mimicing = true;
9153             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9154             if(this.monitorTab){
9155                 this.el.on("keydown", this.checkTab, this);
9156             }
9157         }
9158         */
9159     },
9160
9161     // private
9162     checkTab : function(e){
9163         if(e.getKey() == e.TAB){
9164             this.triggerBlur();
9165         }
9166     },
9167
9168     // private
9169     onBlur : function(){
9170         // do nothing
9171     },
9172
9173     // private
9174     mimicBlur : function(e, t){
9175         /*
9176         if(!this.wrap.contains(t) && this.validateBlur()){
9177             this.triggerBlur();
9178         }
9179         */
9180     },
9181
9182     // private
9183     triggerBlur : function(){
9184         this.mimicing = false;
9185         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9186         if(this.monitorTab){
9187             this.el.un("keydown", this.checkTab, this);
9188         }
9189         //this.wrap.removeClass('x-trigger-wrap-focus');
9190         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9191     },
9192
9193     // private
9194     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9195     validateBlur : function(e, t){
9196         return true;
9197     },
9198
9199     // private
9200     onDisable : function(){
9201         this.inputEl().dom.disabled = true;
9202         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9203         //if(this.wrap){
9204         //    this.wrap.addClass('x-item-disabled');
9205         //}
9206     },
9207
9208     // private
9209     onEnable : function(){
9210         this.inputEl().dom.disabled = false;
9211         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9212         //if(this.wrap){
9213         //    this.el.removeClass('x-item-disabled');
9214         //}
9215     },
9216
9217     // private
9218     onShow : function(){
9219         var ae = this.getActionEl();
9220         
9221         if(ae){
9222             ae.dom.style.display = '';
9223             ae.dom.style.visibility = 'visible';
9224         }
9225     },
9226
9227     // private
9228     
9229     onHide : function(){
9230         var ae = this.getActionEl();
9231         ae.dom.style.display = 'none';
9232     },
9233
9234     /**
9235      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9236      * by an implementing function.
9237      * @method
9238      * @param {EventObject} e
9239      */
9240     onTriggerClick : Roo.emptyFn
9241 });
9242  /*
9243  * Based on:
9244  * Ext JS Library 1.1.1
9245  * Copyright(c) 2006-2007, Ext JS, LLC.
9246  *
9247  * Originally Released Under LGPL - original licence link has changed is not relivant.
9248  *
9249  * Fork - LGPL
9250  * <script type="text/javascript">
9251  */
9252
9253
9254 /**
9255  * @class Roo.data.SortTypes
9256  * @singleton
9257  * Defines the default sorting (casting?) comparison functions used when sorting data.
9258  */
9259 Roo.data.SortTypes = {
9260     /**
9261      * Default sort that does nothing
9262      * @param {Mixed} s The value being converted
9263      * @return {Mixed} The comparison value
9264      */
9265     none : function(s){
9266         return s;
9267     },
9268     
9269     /**
9270      * The regular expression used to strip tags
9271      * @type {RegExp}
9272      * @property
9273      */
9274     stripTagsRE : /<\/?[^>]+>/gi,
9275     
9276     /**
9277      * Strips all HTML tags to sort on text only
9278      * @param {Mixed} s The value being converted
9279      * @return {String} The comparison value
9280      */
9281     asText : function(s){
9282         return String(s).replace(this.stripTagsRE, "");
9283     },
9284     
9285     /**
9286      * Strips all HTML tags to sort on text only - Case insensitive
9287      * @param {Mixed} s The value being converted
9288      * @return {String} The comparison value
9289      */
9290     asUCText : function(s){
9291         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9292     },
9293     
9294     /**
9295      * Case insensitive string
9296      * @param {Mixed} s The value being converted
9297      * @return {String} The comparison value
9298      */
9299     asUCString : function(s) {
9300         return String(s).toUpperCase();
9301     },
9302     
9303     /**
9304      * Date sorting
9305      * @param {Mixed} s The value being converted
9306      * @return {Number} The comparison value
9307      */
9308     asDate : function(s) {
9309         if(!s){
9310             return 0;
9311         }
9312         if(s instanceof Date){
9313             return s.getTime();
9314         }
9315         return Date.parse(String(s));
9316     },
9317     
9318     /**
9319      * Float sorting
9320      * @param {Mixed} s The value being converted
9321      * @return {Float} The comparison value
9322      */
9323     asFloat : function(s) {
9324         var val = parseFloat(String(s).replace(/,/g, ""));
9325         if(isNaN(val)) val = 0;
9326         return val;
9327     },
9328     
9329     /**
9330      * Integer sorting
9331      * @param {Mixed} s The value being converted
9332      * @return {Number} The comparison value
9333      */
9334     asInt : function(s) {
9335         var val = parseInt(String(s).replace(/,/g, ""));
9336         if(isNaN(val)) val = 0;
9337         return val;
9338     }
9339 };/*
9340  * Based on:
9341  * Ext JS Library 1.1.1
9342  * Copyright(c) 2006-2007, Ext JS, LLC.
9343  *
9344  * Originally Released Under LGPL - original licence link has changed is not relivant.
9345  *
9346  * Fork - LGPL
9347  * <script type="text/javascript">
9348  */
9349
9350 /**
9351 * @class Roo.data.Record
9352  * Instances of this class encapsulate both record <em>definition</em> information, and record
9353  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9354  * to access Records cached in an {@link Roo.data.Store} object.<br>
9355  * <p>
9356  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9357  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9358  * objects.<br>
9359  * <p>
9360  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9361  * @constructor
9362  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9363  * {@link #create}. The parameters are the same.
9364  * @param {Array} data An associative Array of data values keyed by the field name.
9365  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9366  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9367  * not specified an integer id is generated.
9368  */
9369 Roo.data.Record = function(data, id){
9370     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9371     this.data = data;
9372 };
9373
9374 /**
9375  * Generate a constructor for a specific record layout.
9376  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9377  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9378  * Each field definition object may contain the following properties: <ul>
9379  * <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,
9380  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9381  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9382  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9383  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9384  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9385  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9386  * this may be omitted.</p></li>
9387  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9388  * <ul><li>auto (Default, implies no conversion)</li>
9389  * <li>string</li>
9390  * <li>int</li>
9391  * <li>float</li>
9392  * <li>boolean</li>
9393  * <li>date</li></ul></p></li>
9394  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9395  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9396  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9397  * by the Reader into an object that will be stored in the Record. It is passed the
9398  * following parameters:<ul>
9399  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9400  * </ul></p></li>
9401  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9402  * </ul>
9403  * <br>usage:<br><pre><code>
9404 var TopicRecord = Roo.data.Record.create(
9405     {name: 'title', mapping: 'topic_title'},
9406     {name: 'author', mapping: 'username'},
9407     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9408     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9409     {name: 'lastPoster', mapping: 'user2'},
9410     {name: 'excerpt', mapping: 'post_text'}
9411 );
9412
9413 var myNewRecord = new TopicRecord({
9414     title: 'Do my job please',
9415     author: 'noobie',
9416     totalPosts: 1,
9417     lastPost: new Date(),
9418     lastPoster: 'Animal',
9419     excerpt: 'No way dude!'
9420 });
9421 myStore.add(myNewRecord);
9422 </code></pre>
9423  * @method create
9424  * @static
9425  */
9426 Roo.data.Record.create = function(o){
9427     var f = function(){
9428         f.superclass.constructor.apply(this, arguments);
9429     };
9430     Roo.extend(f, Roo.data.Record);
9431     var p = f.prototype;
9432     p.fields = new Roo.util.MixedCollection(false, function(field){
9433         return field.name;
9434     });
9435     for(var i = 0, len = o.length; i < len; i++){
9436         p.fields.add(new Roo.data.Field(o[i]));
9437     }
9438     f.getField = function(name){
9439         return p.fields.get(name);  
9440     };
9441     return f;
9442 };
9443
9444 Roo.data.Record.AUTO_ID = 1000;
9445 Roo.data.Record.EDIT = 'edit';
9446 Roo.data.Record.REJECT = 'reject';
9447 Roo.data.Record.COMMIT = 'commit';
9448
9449 Roo.data.Record.prototype = {
9450     /**
9451      * Readonly flag - true if this record has been modified.
9452      * @type Boolean
9453      */
9454     dirty : false,
9455     editing : false,
9456     error: null,
9457     modified: null,
9458
9459     // private
9460     join : function(store){
9461         this.store = store;
9462     },
9463
9464     /**
9465      * Set the named field to the specified value.
9466      * @param {String} name The name of the field to set.
9467      * @param {Object} value The value to set the field to.
9468      */
9469     set : function(name, value){
9470         if(this.data[name] == value){
9471             return;
9472         }
9473         this.dirty = true;
9474         if(!this.modified){
9475             this.modified = {};
9476         }
9477         if(typeof this.modified[name] == 'undefined'){
9478             this.modified[name] = this.data[name];
9479         }
9480         this.data[name] = value;
9481         if(!this.editing && this.store){
9482             this.store.afterEdit(this);
9483         }       
9484     },
9485
9486     /**
9487      * Get the value of the named field.
9488      * @param {String} name The name of the field to get the value of.
9489      * @return {Object} The value of the field.
9490      */
9491     get : function(name){
9492         return this.data[name]; 
9493     },
9494
9495     // private
9496     beginEdit : function(){
9497         this.editing = true;
9498         this.modified = {}; 
9499     },
9500
9501     // private
9502     cancelEdit : function(){
9503         this.editing = false;
9504         delete this.modified;
9505     },
9506
9507     // private
9508     endEdit : function(){
9509         this.editing = false;
9510         if(this.dirty && this.store){
9511             this.store.afterEdit(this);
9512         }
9513     },
9514
9515     /**
9516      * Usually called by the {@link Roo.data.Store} which owns the Record.
9517      * Rejects all changes made to the Record since either creation, or the last commit operation.
9518      * Modified fields are reverted to their original values.
9519      * <p>
9520      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9521      * of reject operations.
9522      */
9523     reject : function(){
9524         var m = this.modified;
9525         for(var n in m){
9526             if(typeof m[n] != "function"){
9527                 this.data[n] = m[n];
9528             }
9529         }
9530         this.dirty = false;
9531         delete this.modified;
9532         this.editing = false;
9533         if(this.store){
9534             this.store.afterReject(this);
9535         }
9536     },
9537
9538     /**
9539      * Usually called by the {@link Roo.data.Store} which owns the Record.
9540      * Commits all changes made to the Record since either creation, or the last commit operation.
9541      * <p>
9542      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9543      * of commit operations.
9544      */
9545     commit : function(){
9546         this.dirty = false;
9547         delete this.modified;
9548         this.editing = false;
9549         if(this.store){
9550             this.store.afterCommit(this);
9551         }
9552     },
9553
9554     // private
9555     hasError : function(){
9556         return this.error != null;
9557     },
9558
9559     // private
9560     clearError : function(){
9561         this.error = null;
9562     },
9563
9564     /**
9565      * Creates a copy of this record.
9566      * @param {String} id (optional) A new record id if you don't want to use this record's id
9567      * @return {Record}
9568      */
9569     copy : function(newId) {
9570         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9571     }
9572 };/*
9573  * Based on:
9574  * Ext JS Library 1.1.1
9575  * Copyright(c) 2006-2007, Ext JS, LLC.
9576  *
9577  * Originally Released Under LGPL - original licence link has changed is not relivant.
9578  *
9579  * Fork - LGPL
9580  * <script type="text/javascript">
9581  */
9582
9583
9584
9585 /**
9586  * @class Roo.data.Store
9587  * @extends Roo.util.Observable
9588  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9589  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9590  * <p>
9591  * 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
9592  * has no knowledge of the format of the data returned by the Proxy.<br>
9593  * <p>
9594  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9595  * instances from the data object. These records are cached and made available through accessor functions.
9596  * @constructor
9597  * Creates a new Store.
9598  * @param {Object} config A config object containing the objects needed for the Store to access data,
9599  * and read the data into Records.
9600  */
9601 Roo.data.Store = function(config){
9602     this.data = new Roo.util.MixedCollection(false);
9603     this.data.getKey = function(o){
9604         return o.id;
9605     };
9606     this.baseParams = {};
9607     // private
9608     this.paramNames = {
9609         "start" : "start",
9610         "limit" : "limit",
9611         "sort" : "sort",
9612         "dir" : "dir",
9613         "multisort" : "_multisort"
9614     };
9615
9616     if(config && config.data){
9617         this.inlineData = config.data;
9618         delete config.data;
9619     }
9620
9621     Roo.apply(this, config);
9622     
9623     if(this.reader){ // reader passed
9624         this.reader = Roo.factory(this.reader, Roo.data);
9625         this.reader.xmodule = this.xmodule || false;
9626         if(!this.recordType){
9627             this.recordType = this.reader.recordType;
9628         }
9629         if(this.reader.onMetaChange){
9630             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9631         }
9632     }
9633
9634     if(this.recordType){
9635         this.fields = this.recordType.prototype.fields;
9636     }
9637     this.modified = [];
9638
9639     this.addEvents({
9640         /**
9641          * @event datachanged
9642          * Fires when the data cache has changed, and a widget which is using this Store
9643          * as a Record cache should refresh its view.
9644          * @param {Store} this
9645          */
9646         datachanged : true,
9647         /**
9648          * @event metachange
9649          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9650          * @param {Store} this
9651          * @param {Object} meta The JSON metadata
9652          */
9653         metachange : true,
9654         /**
9655          * @event add
9656          * Fires when Records have been added to the Store
9657          * @param {Store} this
9658          * @param {Roo.data.Record[]} records The array of Records added
9659          * @param {Number} index The index at which the record(s) were added
9660          */
9661         add : true,
9662         /**
9663          * @event remove
9664          * Fires when a Record has been removed from the Store
9665          * @param {Store} this
9666          * @param {Roo.data.Record} record The Record that was removed
9667          * @param {Number} index The index at which the record was removed
9668          */
9669         remove : true,
9670         /**
9671          * @event update
9672          * Fires when a Record has been updated
9673          * @param {Store} this
9674          * @param {Roo.data.Record} record The Record that was updated
9675          * @param {String} operation The update operation being performed.  Value may be one of:
9676          * <pre><code>
9677  Roo.data.Record.EDIT
9678  Roo.data.Record.REJECT
9679  Roo.data.Record.COMMIT
9680          * </code></pre>
9681          */
9682         update : true,
9683         /**
9684          * @event clear
9685          * Fires when the data cache has been cleared.
9686          * @param {Store} this
9687          */
9688         clear : true,
9689         /**
9690          * @event beforeload
9691          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9692          * the load action will be canceled.
9693          * @param {Store} this
9694          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9695          */
9696         beforeload : true,
9697         /**
9698          * @event beforeloadadd
9699          * Fires after a new set of Records has been loaded.
9700          * @param {Store} this
9701          * @param {Roo.data.Record[]} records The Records that were loaded
9702          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9703          */
9704         beforeloadadd : true,
9705         /**
9706          * @event load
9707          * Fires after a new set of Records has been loaded, before they are added to the store.
9708          * @param {Store} this
9709          * @param {Roo.data.Record[]} records The Records that were loaded
9710          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9711          * @params {Object} return from reader
9712          */
9713         load : true,
9714         /**
9715          * @event loadexception
9716          * Fires if an exception occurs in the Proxy during loading.
9717          * Called with the signature of the Proxy's "loadexception" event.
9718          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9719          * 
9720          * @param {Proxy} 
9721          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9722          * @param {Object} load options 
9723          * @param {Object} jsonData from your request (normally this contains the Exception)
9724          */
9725         loadexception : true
9726     });
9727     
9728     if(this.proxy){
9729         this.proxy = Roo.factory(this.proxy, Roo.data);
9730         this.proxy.xmodule = this.xmodule || false;
9731         this.relayEvents(this.proxy,  ["loadexception"]);
9732     }
9733     this.sortToggle = {};
9734     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9735
9736     Roo.data.Store.superclass.constructor.call(this);
9737
9738     if(this.inlineData){
9739         this.loadData(this.inlineData);
9740         delete this.inlineData;
9741     }
9742 };
9743
9744 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9745      /**
9746     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9747     * without a remote query - used by combo/forms at present.
9748     */
9749     
9750     /**
9751     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9752     */
9753     /**
9754     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9755     */
9756     /**
9757     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9758     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9759     */
9760     /**
9761     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9762     * on any HTTP request
9763     */
9764     /**
9765     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9766     */
9767     /**
9768     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9769     */
9770     multiSort: false,
9771     /**
9772     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9773     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9774     */
9775     remoteSort : false,
9776
9777     /**
9778     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9779      * loaded or when a record is removed. (defaults to false).
9780     */
9781     pruneModifiedRecords : false,
9782
9783     // private
9784     lastOptions : null,
9785
9786     /**
9787      * Add Records to the Store and fires the add event.
9788      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9789      */
9790     add : function(records){
9791         records = [].concat(records);
9792         for(var i = 0, len = records.length; i < len; i++){
9793             records[i].join(this);
9794         }
9795         var index = this.data.length;
9796         this.data.addAll(records);
9797         this.fireEvent("add", this, records, index);
9798     },
9799
9800     /**
9801      * Remove a Record from the Store and fires the remove event.
9802      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9803      */
9804     remove : function(record){
9805         var index = this.data.indexOf(record);
9806         this.data.removeAt(index);
9807         if(this.pruneModifiedRecords){
9808             this.modified.remove(record);
9809         }
9810         this.fireEvent("remove", this, record, index);
9811     },
9812
9813     /**
9814      * Remove all Records from the Store and fires the clear event.
9815      */
9816     removeAll : function(){
9817         this.data.clear();
9818         if(this.pruneModifiedRecords){
9819             this.modified = [];
9820         }
9821         this.fireEvent("clear", this);
9822     },
9823
9824     /**
9825      * Inserts Records to the Store at the given index and fires the add event.
9826      * @param {Number} index The start index at which to insert the passed Records.
9827      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9828      */
9829     insert : function(index, records){
9830         records = [].concat(records);
9831         for(var i = 0, len = records.length; i < len; i++){
9832             this.data.insert(index, records[i]);
9833             records[i].join(this);
9834         }
9835         this.fireEvent("add", this, records, index);
9836     },
9837
9838     /**
9839      * Get the index within the cache of the passed Record.
9840      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9841      * @return {Number} The index of the passed Record. Returns -1 if not found.
9842      */
9843     indexOf : function(record){
9844         return this.data.indexOf(record);
9845     },
9846
9847     /**
9848      * Get the index within the cache of the Record with the passed id.
9849      * @param {String} id The id of the Record to find.
9850      * @return {Number} The index of the Record. Returns -1 if not found.
9851      */
9852     indexOfId : function(id){
9853         return this.data.indexOfKey(id);
9854     },
9855
9856     /**
9857      * Get the Record with the specified id.
9858      * @param {String} id The id of the Record to find.
9859      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9860      */
9861     getById : function(id){
9862         return this.data.key(id);
9863     },
9864
9865     /**
9866      * Get the Record at the specified index.
9867      * @param {Number} index The index of the Record to find.
9868      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9869      */
9870     getAt : function(index){
9871         return this.data.itemAt(index);
9872     },
9873
9874     /**
9875      * Returns a range of Records between specified indices.
9876      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9877      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9878      * @return {Roo.data.Record[]} An array of Records
9879      */
9880     getRange : function(start, end){
9881         return this.data.getRange(start, end);
9882     },
9883
9884     // private
9885     storeOptions : function(o){
9886         o = Roo.apply({}, o);
9887         delete o.callback;
9888         delete o.scope;
9889         this.lastOptions = o;
9890     },
9891
9892     /**
9893      * Loads the Record cache from the configured Proxy using the configured Reader.
9894      * <p>
9895      * If using remote paging, then the first load call must specify the <em>start</em>
9896      * and <em>limit</em> properties in the options.params property to establish the initial
9897      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9898      * <p>
9899      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9900      * and this call will return before the new data has been loaded. Perform any post-processing
9901      * in a callback function, or in a "load" event handler.</strong>
9902      * <p>
9903      * @param {Object} options An object containing properties which control loading options:<ul>
9904      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9905      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9906      * passed the following arguments:<ul>
9907      * <li>r : Roo.data.Record[]</li>
9908      * <li>options: Options object from the load call</li>
9909      * <li>success: Boolean success indicator</li></ul></li>
9910      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9911      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9912      * </ul>
9913      */
9914     load : function(options){
9915         options = options || {};
9916         if(this.fireEvent("beforeload", this, options) !== false){
9917             this.storeOptions(options);
9918             var p = Roo.apply(options.params || {}, this.baseParams);
9919             // if meta was not loaded from remote source.. try requesting it.
9920             if (!this.reader.metaFromRemote) {
9921                 p._requestMeta = 1;
9922             }
9923             if(this.sortInfo && this.remoteSort){
9924                 var pn = this.paramNames;
9925                 p[pn["sort"]] = this.sortInfo.field;
9926                 p[pn["dir"]] = this.sortInfo.direction;
9927             }
9928             if (this.multiSort) {
9929                 var pn = this.paramNames;
9930                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9931             }
9932             
9933             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9934         }
9935     },
9936
9937     /**
9938      * Reloads the Record cache from the configured Proxy using the configured Reader and
9939      * the options from the last load operation performed.
9940      * @param {Object} options (optional) An object containing properties which may override the options
9941      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9942      * the most recently used options are reused).
9943      */
9944     reload : function(options){
9945         this.load(Roo.applyIf(options||{}, this.lastOptions));
9946     },
9947
9948     // private
9949     // Called as a callback by the Reader during a load operation.
9950     loadRecords : function(o, options, success){
9951         if(!o || success === false){
9952             if(success !== false){
9953                 this.fireEvent("load", this, [], options, o);
9954             }
9955             if(options.callback){
9956                 options.callback.call(options.scope || this, [], options, false);
9957             }
9958             return;
9959         }
9960         // if data returned failure - throw an exception.
9961         if (o.success === false) {
9962             // show a message if no listener is registered.
9963             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9964                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9965             }
9966             // loadmask wil be hooked into this..
9967             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9968             return;
9969         }
9970         var r = o.records, t = o.totalRecords || r.length;
9971         
9972         this.fireEvent("beforeloadadd", this, r, options, o);
9973         
9974         if(!options || options.add !== true){
9975             if(this.pruneModifiedRecords){
9976                 this.modified = [];
9977             }
9978             for(var i = 0, len = r.length; i < len; i++){
9979                 r[i].join(this);
9980             }
9981             if(this.snapshot){
9982                 this.data = this.snapshot;
9983                 delete this.snapshot;
9984             }
9985             this.data.clear();
9986             this.data.addAll(r);
9987             this.totalLength = t;
9988             this.applySort();
9989             this.fireEvent("datachanged", this);
9990         }else{
9991             this.totalLength = Math.max(t, this.data.length+r.length);
9992             this.add(r);
9993         }
9994         this.fireEvent("load", this, r, options, o);
9995         if(options.callback){
9996             options.callback.call(options.scope || this, r, options, true);
9997         }
9998     },
9999
10000
10001     /**
10002      * Loads data from a passed data block. A Reader which understands the format of the data
10003      * must have been configured in the constructor.
10004      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10005      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10006      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10007      */
10008     loadData : function(o, append){
10009         var r = this.reader.readRecords(o);
10010         this.loadRecords(r, {add: append}, true);
10011     },
10012
10013     /**
10014      * Gets the number of cached records.
10015      * <p>
10016      * <em>If using paging, this may not be the total size of the dataset. If the data object
10017      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10018      * the data set size</em>
10019      */
10020     getCount : function(){
10021         return this.data.length || 0;
10022     },
10023
10024     /**
10025      * Gets the total number of records in the dataset as returned by the server.
10026      * <p>
10027      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10028      * the dataset size</em>
10029      */
10030     getTotalCount : function(){
10031         return this.totalLength || 0;
10032     },
10033
10034     /**
10035      * Returns the sort state of the Store as an object with two properties:
10036      * <pre><code>
10037  field {String} The name of the field by which the Records are sorted
10038  direction {String} The sort order, "ASC" or "DESC"
10039      * </code></pre>
10040      */
10041     getSortState : function(){
10042         return this.sortInfo;
10043     },
10044
10045     // private
10046     applySort : function(){
10047         if(this.sortInfo && !this.remoteSort){
10048             var s = this.sortInfo, f = s.field;
10049             var st = this.fields.get(f).sortType;
10050             var fn = function(r1, r2){
10051                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10052                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10053             };
10054             this.data.sort(s.direction, fn);
10055             if(this.snapshot && this.snapshot != this.data){
10056                 this.snapshot.sort(s.direction, fn);
10057             }
10058         }
10059     },
10060
10061     /**
10062      * Sets the default sort column and order to be used by the next load operation.
10063      * @param {String} fieldName The name of the field to sort by.
10064      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10065      */
10066     setDefaultSort : function(field, dir){
10067         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10068     },
10069
10070     /**
10071      * Sort the Records.
10072      * If remote sorting is used, the sort is performed on the server, and the cache is
10073      * reloaded. If local sorting is used, the cache is sorted internally.
10074      * @param {String} fieldName The name of the field to sort by.
10075      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10076      */
10077     sort : function(fieldName, dir){
10078         var f = this.fields.get(fieldName);
10079         if(!dir){
10080             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10081             
10082             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10083                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10084             }else{
10085                 dir = f.sortDir;
10086             }
10087         }
10088         this.sortToggle[f.name] = dir;
10089         this.sortInfo = {field: f.name, direction: dir};
10090         if(!this.remoteSort){
10091             this.applySort();
10092             this.fireEvent("datachanged", this);
10093         }else{
10094             this.load(this.lastOptions);
10095         }
10096     },
10097
10098     /**
10099      * Calls the specified function for each of the Records in the cache.
10100      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10101      * Returning <em>false</em> aborts and exits the iteration.
10102      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10103      */
10104     each : function(fn, scope){
10105         this.data.each(fn, scope);
10106     },
10107
10108     /**
10109      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10110      * (e.g., during paging).
10111      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10112      */
10113     getModifiedRecords : function(){
10114         return this.modified;
10115     },
10116
10117     // private
10118     createFilterFn : function(property, value, anyMatch){
10119         if(!value.exec){ // not a regex
10120             value = String(value);
10121             if(value.length == 0){
10122                 return false;
10123             }
10124             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10125         }
10126         return function(r){
10127             return value.test(r.data[property]);
10128         };
10129     },
10130
10131     /**
10132      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10133      * @param {String} property A field on your records
10134      * @param {Number} start The record index to start at (defaults to 0)
10135      * @param {Number} end The last record index to include (defaults to length - 1)
10136      * @return {Number} The sum
10137      */
10138     sum : function(property, start, end){
10139         var rs = this.data.items, v = 0;
10140         start = start || 0;
10141         end = (end || end === 0) ? end : rs.length-1;
10142
10143         for(var i = start; i <= end; i++){
10144             v += (rs[i].data[property] || 0);
10145         }
10146         return v;
10147     },
10148
10149     /**
10150      * Filter the records by a specified property.
10151      * @param {String} field A field on your records
10152      * @param {String/RegExp} value Either a string that the field
10153      * should start with or a RegExp to test against the field
10154      * @param {Boolean} anyMatch True to match any part not just the beginning
10155      */
10156     filter : function(property, value, anyMatch){
10157         var fn = this.createFilterFn(property, value, anyMatch);
10158         return fn ? this.filterBy(fn) : this.clearFilter();
10159     },
10160
10161     /**
10162      * Filter by a function. The specified function will be called with each
10163      * record in this data source. If the function returns true the record is included,
10164      * otherwise it is filtered.
10165      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10166      * @param {Object} scope (optional) The scope of the function (defaults to this)
10167      */
10168     filterBy : function(fn, scope){
10169         this.snapshot = this.snapshot || this.data;
10170         this.data = this.queryBy(fn, scope||this);
10171         this.fireEvent("datachanged", this);
10172     },
10173
10174     /**
10175      * Query the records by a specified property.
10176      * @param {String} field A field on your records
10177      * @param {String/RegExp} value Either a string that the field
10178      * should start with or a RegExp to test against the field
10179      * @param {Boolean} anyMatch True to match any part not just the beginning
10180      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10181      */
10182     query : function(property, value, anyMatch){
10183         var fn = this.createFilterFn(property, value, anyMatch);
10184         return fn ? this.queryBy(fn) : this.data.clone();
10185     },
10186
10187     /**
10188      * Query by a function. The specified function will be called with each
10189      * record in this data source. If the function returns true the record is included
10190      * in the results.
10191      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10192      * @param {Object} scope (optional) The scope of the function (defaults to this)
10193       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10194      **/
10195     queryBy : function(fn, scope){
10196         var data = this.snapshot || this.data;
10197         return data.filterBy(fn, scope||this);
10198     },
10199
10200     /**
10201      * Collects unique values for a particular dataIndex from this store.
10202      * @param {String} dataIndex The property to collect
10203      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10204      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10205      * @return {Array} An array of the unique values
10206      **/
10207     collect : function(dataIndex, allowNull, bypassFilter){
10208         var d = (bypassFilter === true && this.snapshot) ?
10209                 this.snapshot.items : this.data.items;
10210         var v, sv, r = [], l = {};
10211         for(var i = 0, len = d.length; i < len; i++){
10212             v = d[i].data[dataIndex];
10213             sv = String(v);
10214             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10215                 l[sv] = true;
10216                 r[r.length] = v;
10217             }
10218         }
10219         return r;
10220     },
10221
10222     /**
10223      * Revert to a view of the Record cache with no filtering applied.
10224      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10225      */
10226     clearFilter : function(suppressEvent){
10227         if(this.snapshot && this.snapshot != this.data){
10228             this.data = this.snapshot;
10229             delete this.snapshot;
10230             if(suppressEvent !== true){
10231                 this.fireEvent("datachanged", this);
10232             }
10233         }
10234     },
10235
10236     // private
10237     afterEdit : function(record){
10238         if(this.modified.indexOf(record) == -1){
10239             this.modified.push(record);
10240         }
10241         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10242     },
10243     
10244     // private
10245     afterReject : function(record){
10246         this.modified.remove(record);
10247         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10248     },
10249
10250     // private
10251     afterCommit : function(record){
10252         this.modified.remove(record);
10253         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10254     },
10255
10256     /**
10257      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10258      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10259      */
10260     commitChanges : function(){
10261         var m = this.modified.slice(0);
10262         this.modified = [];
10263         for(var i = 0, len = m.length; i < len; i++){
10264             m[i].commit();
10265         }
10266     },
10267
10268     /**
10269      * Cancel outstanding changes on all changed records.
10270      */
10271     rejectChanges : function(){
10272         var m = this.modified.slice(0);
10273         this.modified = [];
10274         for(var i = 0, len = m.length; i < len; i++){
10275             m[i].reject();
10276         }
10277     },
10278
10279     onMetaChange : function(meta, rtype, o){
10280         this.recordType = rtype;
10281         this.fields = rtype.prototype.fields;
10282         delete this.snapshot;
10283         this.sortInfo = meta.sortInfo || this.sortInfo;
10284         this.modified = [];
10285         this.fireEvent('metachange', this, this.reader.meta);
10286     },
10287     
10288     moveIndex : function(data, type)
10289     {
10290         var index = this.indexOf(data);
10291         
10292         var newIndex = index + type;
10293         
10294         this.remove(data);
10295         
10296         this.insert(newIndex, data);
10297         
10298     }
10299 });/*
10300  * Based on:
10301  * Ext JS Library 1.1.1
10302  * Copyright(c) 2006-2007, Ext JS, LLC.
10303  *
10304  * Originally Released Under LGPL - original licence link has changed is not relivant.
10305  *
10306  * Fork - LGPL
10307  * <script type="text/javascript">
10308  */
10309
10310 /**
10311  * @class Roo.data.SimpleStore
10312  * @extends Roo.data.Store
10313  * Small helper class to make creating Stores from Array data easier.
10314  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10315  * @cfg {Array} fields An array of field definition objects, or field name strings.
10316  * @cfg {Array} data The multi-dimensional array of data
10317  * @constructor
10318  * @param {Object} config
10319  */
10320 Roo.data.SimpleStore = function(config){
10321     Roo.data.SimpleStore.superclass.constructor.call(this, {
10322         isLocal : true,
10323         reader: new Roo.data.ArrayReader({
10324                 id: config.id
10325             },
10326             Roo.data.Record.create(config.fields)
10327         ),
10328         proxy : new Roo.data.MemoryProxy(config.data)
10329     });
10330     this.load();
10331 };
10332 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10333  * Based on:
10334  * Ext JS Library 1.1.1
10335  * Copyright(c) 2006-2007, Ext JS, LLC.
10336  *
10337  * Originally Released Under LGPL - original licence link has changed is not relivant.
10338  *
10339  * Fork - LGPL
10340  * <script type="text/javascript">
10341  */
10342
10343 /**
10344 /**
10345  * @extends Roo.data.Store
10346  * @class Roo.data.JsonStore
10347  * Small helper class to make creating Stores for JSON data easier. <br/>
10348 <pre><code>
10349 var store = new Roo.data.JsonStore({
10350     url: 'get-images.php',
10351     root: 'images',
10352     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10353 });
10354 </code></pre>
10355  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10356  * JsonReader and HttpProxy (unless inline data is provided).</b>
10357  * @cfg {Array} fields An array of field definition objects, or field name strings.
10358  * @constructor
10359  * @param {Object} config
10360  */
10361 Roo.data.JsonStore = function(c){
10362     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10363         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10364         reader: new Roo.data.JsonReader(c, c.fields)
10365     }));
10366 };
10367 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10368  * Based on:
10369  * Ext JS Library 1.1.1
10370  * Copyright(c) 2006-2007, Ext JS, LLC.
10371  *
10372  * Originally Released Under LGPL - original licence link has changed is not relivant.
10373  *
10374  * Fork - LGPL
10375  * <script type="text/javascript">
10376  */
10377
10378  
10379 Roo.data.Field = function(config){
10380     if(typeof config == "string"){
10381         config = {name: config};
10382     }
10383     Roo.apply(this, config);
10384     
10385     if(!this.type){
10386         this.type = "auto";
10387     }
10388     
10389     var st = Roo.data.SortTypes;
10390     // named sortTypes are supported, here we look them up
10391     if(typeof this.sortType == "string"){
10392         this.sortType = st[this.sortType];
10393     }
10394     
10395     // set default sortType for strings and dates
10396     if(!this.sortType){
10397         switch(this.type){
10398             case "string":
10399                 this.sortType = st.asUCString;
10400                 break;
10401             case "date":
10402                 this.sortType = st.asDate;
10403                 break;
10404             default:
10405                 this.sortType = st.none;
10406         }
10407     }
10408
10409     // define once
10410     var stripRe = /[\$,%]/g;
10411
10412     // prebuilt conversion function for this field, instead of
10413     // switching every time we're reading a value
10414     if(!this.convert){
10415         var cv, dateFormat = this.dateFormat;
10416         switch(this.type){
10417             case "":
10418             case "auto":
10419             case undefined:
10420                 cv = function(v){ return v; };
10421                 break;
10422             case "string":
10423                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10424                 break;
10425             case "int":
10426                 cv = function(v){
10427                     return v !== undefined && v !== null && v !== '' ?
10428                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10429                     };
10430                 break;
10431             case "float":
10432                 cv = function(v){
10433                     return v !== undefined && v !== null && v !== '' ?
10434                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10435                     };
10436                 break;
10437             case "bool":
10438             case "boolean":
10439                 cv = function(v){ return v === true || v === "true" || v == 1; };
10440                 break;
10441             case "date":
10442                 cv = function(v){
10443                     if(!v){
10444                         return '';
10445                     }
10446                     if(v instanceof Date){
10447                         return v;
10448                     }
10449                     if(dateFormat){
10450                         if(dateFormat == "timestamp"){
10451                             return new Date(v*1000);
10452                         }
10453                         return Date.parseDate(v, dateFormat);
10454                     }
10455                     var parsed = Date.parse(v);
10456                     return parsed ? new Date(parsed) : null;
10457                 };
10458              break;
10459             
10460         }
10461         this.convert = cv;
10462     }
10463 };
10464
10465 Roo.data.Field.prototype = {
10466     dateFormat: null,
10467     defaultValue: "",
10468     mapping: null,
10469     sortType : null,
10470     sortDir : "ASC"
10471 };/*
10472  * Based on:
10473  * Ext JS Library 1.1.1
10474  * Copyright(c) 2006-2007, Ext JS, LLC.
10475  *
10476  * Originally Released Under LGPL - original licence link has changed is not relivant.
10477  *
10478  * Fork - LGPL
10479  * <script type="text/javascript">
10480  */
10481  
10482 // Base class for reading structured data from a data source.  This class is intended to be
10483 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10484
10485 /**
10486  * @class Roo.data.DataReader
10487  * Base class for reading structured data from a data source.  This class is intended to be
10488  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10489  */
10490
10491 Roo.data.DataReader = function(meta, recordType){
10492     
10493     this.meta = meta;
10494     
10495     this.recordType = recordType instanceof Array ? 
10496         Roo.data.Record.create(recordType) : recordType;
10497 };
10498
10499 Roo.data.DataReader.prototype = {
10500      /**
10501      * Create an empty record
10502      * @param {Object} data (optional) - overlay some values
10503      * @return {Roo.data.Record} record created.
10504      */
10505     newRow :  function(d) {
10506         var da =  {};
10507         this.recordType.prototype.fields.each(function(c) {
10508             switch( c.type) {
10509                 case 'int' : da[c.name] = 0; break;
10510                 case 'date' : da[c.name] = new Date(); break;
10511                 case 'float' : da[c.name] = 0.0; break;
10512                 case 'boolean' : da[c.name] = false; break;
10513                 default : da[c.name] = ""; break;
10514             }
10515             
10516         });
10517         return new this.recordType(Roo.apply(da, d));
10518     }
10519     
10520 };/*
10521  * Based on:
10522  * Ext JS Library 1.1.1
10523  * Copyright(c) 2006-2007, Ext JS, LLC.
10524  *
10525  * Originally Released Under LGPL - original licence link has changed is not relivant.
10526  *
10527  * Fork - LGPL
10528  * <script type="text/javascript">
10529  */
10530
10531 /**
10532  * @class Roo.data.DataProxy
10533  * @extends Roo.data.Observable
10534  * This class is an abstract base class for implementations which provide retrieval of
10535  * unformatted data objects.<br>
10536  * <p>
10537  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10538  * (of the appropriate type which knows how to parse the data object) to provide a block of
10539  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10540  * <p>
10541  * Custom implementations must implement the load method as described in
10542  * {@link Roo.data.HttpProxy#load}.
10543  */
10544 Roo.data.DataProxy = function(){
10545     this.addEvents({
10546         /**
10547          * @event beforeload
10548          * Fires before a network request is made to retrieve a data object.
10549          * @param {Object} This DataProxy object.
10550          * @param {Object} params The params parameter to the load function.
10551          */
10552         beforeload : true,
10553         /**
10554          * @event load
10555          * Fires before the load method's callback is called.
10556          * @param {Object} This DataProxy object.
10557          * @param {Object} o The data object.
10558          * @param {Object} arg The callback argument object passed to the load function.
10559          */
10560         load : true,
10561         /**
10562          * @event loadexception
10563          * Fires if an Exception occurs during data retrieval.
10564          * @param {Object} This DataProxy object.
10565          * @param {Object} o The data object.
10566          * @param {Object} arg The callback argument object passed to the load function.
10567          * @param {Object} e The Exception.
10568          */
10569         loadexception : true
10570     });
10571     Roo.data.DataProxy.superclass.constructor.call(this);
10572 };
10573
10574 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10575
10576     /**
10577      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10578      */
10579 /*
10580  * Based on:
10581  * Ext JS Library 1.1.1
10582  * Copyright(c) 2006-2007, Ext JS, LLC.
10583  *
10584  * Originally Released Under LGPL - original licence link has changed is not relivant.
10585  *
10586  * Fork - LGPL
10587  * <script type="text/javascript">
10588  */
10589 /**
10590  * @class Roo.data.MemoryProxy
10591  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10592  * to the Reader when its load method is called.
10593  * @constructor
10594  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10595  */
10596 Roo.data.MemoryProxy = function(data){
10597     if (data.data) {
10598         data = data.data;
10599     }
10600     Roo.data.MemoryProxy.superclass.constructor.call(this);
10601     this.data = data;
10602 };
10603
10604 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10605     /**
10606      * Load data from the requested source (in this case an in-memory
10607      * data object passed to the constructor), read the data object into
10608      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10609      * process that block using the passed callback.
10610      * @param {Object} params This parameter is not used by the MemoryProxy class.
10611      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10612      * object into a block of Roo.data.Records.
10613      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10614      * The function must be passed <ul>
10615      * <li>The Record block object</li>
10616      * <li>The "arg" argument from the load function</li>
10617      * <li>A boolean success indicator</li>
10618      * </ul>
10619      * @param {Object} scope The scope in which to call the callback
10620      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10621      */
10622     load : function(params, reader, callback, scope, arg){
10623         params = params || {};
10624         var result;
10625         try {
10626             result = reader.readRecords(this.data);
10627         }catch(e){
10628             this.fireEvent("loadexception", this, arg, null, e);
10629             callback.call(scope, null, arg, false);
10630             return;
10631         }
10632         callback.call(scope, result, arg, true);
10633     },
10634     
10635     // private
10636     update : function(params, records){
10637         
10638     }
10639 });/*
10640  * Based on:
10641  * Ext JS Library 1.1.1
10642  * Copyright(c) 2006-2007, Ext JS, LLC.
10643  *
10644  * Originally Released Under LGPL - original licence link has changed is not relivant.
10645  *
10646  * Fork - LGPL
10647  * <script type="text/javascript">
10648  */
10649 /**
10650  * @class Roo.data.HttpProxy
10651  * @extends Roo.data.DataProxy
10652  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10653  * configured to reference a certain URL.<br><br>
10654  * <p>
10655  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10656  * from which the running page was served.<br><br>
10657  * <p>
10658  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10659  * <p>
10660  * Be aware that to enable the browser to parse an XML document, the server must set
10661  * the Content-Type header in the HTTP response to "text/xml".
10662  * @constructor
10663  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10664  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10665  * will be used to make the request.
10666  */
10667 Roo.data.HttpProxy = function(conn){
10668     Roo.data.HttpProxy.superclass.constructor.call(this);
10669     // is conn a conn config or a real conn?
10670     this.conn = conn;
10671     this.useAjax = !conn || !conn.events;
10672   
10673 };
10674
10675 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10676     // thse are take from connection...
10677     
10678     /**
10679      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10680      */
10681     /**
10682      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10683      * extra parameters to each request made by this object. (defaults to undefined)
10684      */
10685     /**
10686      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10687      *  to each request made by this object. (defaults to undefined)
10688      */
10689     /**
10690      * @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)
10691      */
10692     /**
10693      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10694      */
10695      /**
10696      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10697      * @type Boolean
10698      */
10699   
10700
10701     /**
10702      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10703      * @type Boolean
10704      */
10705     /**
10706      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10707      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10708      * a finer-grained basis than the DataProxy events.
10709      */
10710     getConnection : function(){
10711         return this.useAjax ? Roo.Ajax : this.conn;
10712     },
10713
10714     /**
10715      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10716      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10717      * process that block using the passed callback.
10718      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10719      * for the request to the remote server.
10720      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10721      * object into a block of Roo.data.Records.
10722      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10723      * The function must be passed <ul>
10724      * <li>The Record block object</li>
10725      * <li>The "arg" argument from the load function</li>
10726      * <li>A boolean success indicator</li>
10727      * </ul>
10728      * @param {Object} scope The scope in which to call the callback
10729      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10730      */
10731     load : function(params, reader, callback, scope, arg){
10732         if(this.fireEvent("beforeload", this, params) !== false){
10733             var  o = {
10734                 params : params || {},
10735                 request: {
10736                     callback : callback,
10737                     scope : scope,
10738                     arg : arg
10739                 },
10740                 reader: reader,
10741                 callback : this.loadResponse,
10742                 scope: this
10743             };
10744             if(this.useAjax){
10745                 Roo.applyIf(o, this.conn);
10746                 if(this.activeRequest){
10747                     Roo.Ajax.abort(this.activeRequest);
10748                 }
10749                 this.activeRequest = Roo.Ajax.request(o);
10750             }else{
10751                 this.conn.request(o);
10752             }
10753         }else{
10754             callback.call(scope||this, null, arg, false);
10755         }
10756     },
10757
10758     // private
10759     loadResponse : function(o, success, response){
10760         delete this.activeRequest;
10761         if(!success){
10762             this.fireEvent("loadexception", this, o, response);
10763             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10764             return;
10765         }
10766         var result;
10767         try {
10768             result = o.reader.read(response);
10769         }catch(e){
10770             this.fireEvent("loadexception", this, o, response, e);
10771             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10772             return;
10773         }
10774         
10775         this.fireEvent("load", this, o, o.request.arg);
10776         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10777     },
10778
10779     // private
10780     update : function(dataSet){
10781
10782     },
10783
10784     // private
10785     updateResponse : function(dataSet){
10786
10787     }
10788 });/*
10789  * Based on:
10790  * Ext JS Library 1.1.1
10791  * Copyright(c) 2006-2007, Ext JS, LLC.
10792  *
10793  * Originally Released Under LGPL - original licence link has changed is not relivant.
10794  *
10795  * Fork - LGPL
10796  * <script type="text/javascript">
10797  */
10798
10799 /**
10800  * @class Roo.data.ScriptTagProxy
10801  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10802  * other than the originating domain of the running page.<br><br>
10803  * <p>
10804  * <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
10805  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10806  * <p>
10807  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10808  * source code that is used as the source inside a &lt;script> tag.<br><br>
10809  * <p>
10810  * In order for the browser to process the returned data, the server must wrap the data object
10811  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10812  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10813  * depending on whether the callback name was passed:
10814  * <p>
10815  * <pre><code>
10816 boolean scriptTag = false;
10817 String cb = request.getParameter("callback");
10818 if (cb != null) {
10819     scriptTag = true;
10820     response.setContentType("text/javascript");
10821 } else {
10822     response.setContentType("application/x-json");
10823 }
10824 Writer out = response.getWriter();
10825 if (scriptTag) {
10826     out.write(cb + "(");
10827 }
10828 out.print(dataBlock.toJsonString());
10829 if (scriptTag) {
10830     out.write(");");
10831 }
10832 </pre></code>
10833  *
10834  * @constructor
10835  * @param {Object} config A configuration object.
10836  */
10837 Roo.data.ScriptTagProxy = function(config){
10838     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10839     Roo.apply(this, config);
10840     this.head = document.getElementsByTagName("head")[0];
10841 };
10842
10843 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10844
10845 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10846     /**
10847      * @cfg {String} url The URL from which to request the data object.
10848      */
10849     /**
10850      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10851      */
10852     timeout : 30000,
10853     /**
10854      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10855      * the server the name of the callback function set up by the load call to process the returned data object.
10856      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10857      * javascript output which calls this named function passing the data object as its only parameter.
10858      */
10859     callbackParam : "callback",
10860     /**
10861      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10862      * name to the request.
10863      */
10864     nocache : true,
10865
10866     /**
10867      * Load data from the configured URL, read the data object into
10868      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10869      * process that block using the passed callback.
10870      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10871      * for the request to the remote server.
10872      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10873      * object into a block of Roo.data.Records.
10874      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10875      * The function must be passed <ul>
10876      * <li>The Record block object</li>
10877      * <li>The "arg" argument from the load function</li>
10878      * <li>A boolean success indicator</li>
10879      * </ul>
10880      * @param {Object} scope The scope in which to call the callback
10881      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10882      */
10883     load : function(params, reader, callback, scope, arg){
10884         if(this.fireEvent("beforeload", this, params) !== false){
10885
10886             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10887
10888             var url = this.url;
10889             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10890             if(this.nocache){
10891                 url += "&_dc=" + (new Date().getTime());
10892             }
10893             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10894             var trans = {
10895                 id : transId,
10896                 cb : "stcCallback"+transId,
10897                 scriptId : "stcScript"+transId,
10898                 params : params,
10899                 arg : arg,
10900                 url : url,
10901                 callback : callback,
10902                 scope : scope,
10903                 reader : reader
10904             };
10905             var conn = this;
10906
10907             window[trans.cb] = function(o){
10908                 conn.handleResponse(o, trans);
10909             };
10910
10911             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10912
10913             if(this.autoAbort !== false){
10914                 this.abort();
10915             }
10916
10917             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10918
10919             var script = document.createElement("script");
10920             script.setAttribute("src", url);
10921             script.setAttribute("type", "text/javascript");
10922             script.setAttribute("id", trans.scriptId);
10923             this.head.appendChild(script);
10924
10925             this.trans = trans;
10926         }else{
10927             callback.call(scope||this, null, arg, false);
10928         }
10929     },
10930
10931     // private
10932     isLoading : function(){
10933         return this.trans ? true : false;
10934     },
10935
10936     /**
10937      * Abort the current server request.
10938      */
10939     abort : function(){
10940         if(this.isLoading()){
10941             this.destroyTrans(this.trans);
10942         }
10943     },
10944
10945     // private
10946     destroyTrans : function(trans, isLoaded){
10947         this.head.removeChild(document.getElementById(trans.scriptId));
10948         clearTimeout(trans.timeoutId);
10949         if(isLoaded){
10950             window[trans.cb] = undefined;
10951             try{
10952                 delete window[trans.cb];
10953             }catch(e){}
10954         }else{
10955             // if hasn't been loaded, wait for load to remove it to prevent script error
10956             window[trans.cb] = function(){
10957                 window[trans.cb] = undefined;
10958                 try{
10959                     delete window[trans.cb];
10960                 }catch(e){}
10961             };
10962         }
10963     },
10964
10965     // private
10966     handleResponse : function(o, trans){
10967         this.trans = false;
10968         this.destroyTrans(trans, true);
10969         var result;
10970         try {
10971             result = trans.reader.readRecords(o);
10972         }catch(e){
10973             this.fireEvent("loadexception", this, o, trans.arg, e);
10974             trans.callback.call(trans.scope||window, null, trans.arg, false);
10975             return;
10976         }
10977         this.fireEvent("load", this, o, trans.arg);
10978         trans.callback.call(trans.scope||window, result, trans.arg, true);
10979     },
10980
10981     // private
10982     handleFailure : function(trans){
10983         this.trans = false;
10984         this.destroyTrans(trans, false);
10985         this.fireEvent("loadexception", this, null, trans.arg);
10986         trans.callback.call(trans.scope||window, null, trans.arg, false);
10987     }
10988 });/*
10989  * Based on:
10990  * Ext JS Library 1.1.1
10991  * Copyright(c) 2006-2007, Ext JS, LLC.
10992  *
10993  * Originally Released Under LGPL - original licence link has changed is not relivant.
10994  *
10995  * Fork - LGPL
10996  * <script type="text/javascript">
10997  */
10998
10999 /**
11000  * @class Roo.data.JsonReader
11001  * @extends Roo.data.DataReader
11002  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11003  * based on mappings in a provided Roo.data.Record constructor.
11004  * 
11005  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11006  * in the reply previously. 
11007  * 
11008  * <p>
11009  * Example code:
11010  * <pre><code>
11011 var RecordDef = Roo.data.Record.create([
11012     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11013     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11014 ]);
11015 var myReader = new Roo.data.JsonReader({
11016     totalProperty: "results",    // The property which contains the total dataset size (optional)
11017     root: "rows",                // The property which contains an Array of row objects
11018     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11019 }, RecordDef);
11020 </code></pre>
11021  * <p>
11022  * This would consume a JSON file like this:
11023  * <pre><code>
11024 { 'results': 2, 'rows': [
11025     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11026     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11027 }
11028 </code></pre>
11029  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11030  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11031  * paged from the remote server.
11032  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11033  * @cfg {String} root name of the property which contains the Array of row objects.
11034  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11035  * @cfg {Array} fields Array of field definition objects
11036  * @constructor
11037  * Create a new JsonReader
11038  * @param {Object} meta Metadata configuration options
11039  * @param {Object} recordType Either an Array of field definition objects,
11040  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11041  */
11042 Roo.data.JsonReader = function(meta, recordType){
11043     
11044     meta = meta || {};
11045     // set some defaults:
11046     Roo.applyIf(meta, {
11047         totalProperty: 'total',
11048         successProperty : 'success',
11049         root : 'data',
11050         id : 'id'
11051     });
11052     
11053     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11054 };
11055 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11056     
11057     /**
11058      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11059      * Used by Store query builder to append _requestMeta to params.
11060      * 
11061      */
11062     metaFromRemote : false,
11063     /**
11064      * This method is only used by a DataProxy which has retrieved data from a remote server.
11065      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11066      * @return {Object} data A data block which is used by an Roo.data.Store object as
11067      * a cache of Roo.data.Records.
11068      */
11069     read : function(response){
11070         var json = response.responseText;
11071        
11072         var o = /* eval:var:o */ eval("("+json+")");
11073         if(!o) {
11074             throw {message: "JsonReader.read: Json object not found"};
11075         }
11076         
11077         if(o.metaData){
11078             
11079             delete this.ef;
11080             this.metaFromRemote = true;
11081             this.meta = o.metaData;
11082             this.recordType = Roo.data.Record.create(o.metaData.fields);
11083             this.onMetaChange(this.meta, this.recordType, o);
11084         }
11085         return this.readRecords(o);
11086     },
11087
11088     // private function a store will implement
11089     onMetaChange : function(meta, recordType, o){
11090
11091     },
11092
11093     /**
11094          * @ignore
11095          */
11096     simpleAccess: function(obj, subsc) {
11097         return obj[subsc];
11098     },
11099
11100         /**
11101          * @ignore
11102          */
11103     getJsonAccessor: function(){
11104         var re = /[\[\.]/;
11105         return function(expr) {
11106             try {
11107                 return(re.test(expr))
11108                     ? new Function("obj", "return obj." + expr)
11109                     : function(obj){
11110                         return obj[expr];
11111                     };
11112             } catch(e){}
11113             return Roo.emptyFn;
11114         };
11115     }(),
11116
11117     /**
11118      * Create a data block containing Roo.data.Records from an XML document.
11119      * @param {Object} o An object which contains an Array of row objects in the property specified
11120      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11121      * which contains the total size of the dataset.
11122      * @return {Object} data A data block which is used by an Roo.data.Store object as
11123      * a cache of Roo.data.Records.
11124      */
11125     readRecords : function(o){
11126         /**
11127          * After any data loads, the raw JSON data is available for further custom processing.
11128          * @type Object
11129          */
11130         this.o = o;
11131         var s = this.meta, Record = this.recordType,
11132             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11133
11134 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11135         if (!this.ef) {
11136             if(s.totalProperty) {
11137                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11138                 }
11139                 if(s.successProperty) {
11140                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11141                 }
11142                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11143                 if (s.id) {
11144                         var g = this.getJsonAccessor(s.id);
11145                         this.getId = function(rec) {
11146                                 var r = g(rec);  
11147                                 return (r === undefined || r === "") ? null : r;
11148                         };
11149                 } else {
11150                         this.getId = function(){return null;};
11151                 }
11152             this.ef = [];
11153             for(var jj = 0; jj < fl; jj++){
11154                 f = fi[jj];
11155                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11156                 this.ef[jj] = this.getJsonAccessor(map);
11157             }
11158         }
11159
11160         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11161         if(s.totalProperty){
11162             var vt = parseInt(this.getTotal(o), 10);
11163             if(!isNaN(vt)){
11164                 totalRecords = vt;
11165             }
11166         }
11167         if(s.successProperty){
11168             var vs = this.getSuccess(o);
11169             if(vs === false || vs === 'false'){
11170                 success = false;
11171             }
11172         }
11173         var records = [];
11174         for(var i = 0; i < c; i++){
11175                 var n = root[i];
11176             var values = {};
11177             var id = this.getId(n);
11178             for(var j = 0; j < fl; j++){
11179                 f = fi[j];
11180             var v = this.ef[j](n);
11181             if (!f.convert) {
11182                 Roo.log('missing convert for ' + f.name);
11183                 Roo.log(f);
11184                 continue;
11185             }
11186             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11187             }
11188             var record = new Record(values, id);
11189             record.json = n;
11190             records[i] = record;
11191         }
11192         return {
11193             raw : o,
11194             success : success,
11195             records : records,
11196             totalRecords : totalRecords
11197         };
11198     }
11199 });/*
11200  * Based on:
11201  * Ext JS Library 1.1.1
11202  * Copyright(c) 2006-2007, Ext JS, LLC.
11203  *
11204  * Originally Released Under LGPL - original licence link has changed is not relivant.
11205  *
11206  * Fork - LGPL
11207  * <script type="text/javascript">
11208  */
11209
11210 /**
11211  * @class Roo.data.ArrayReader
11212  * @extends Roo.data.DataReader
11213  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11214  * Each element of that Array represents a row of data fields. The
11215  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11216  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11217  * <p>
11218  * Example code:.
11219  * <pre><code>
11220 var RecordDef = Roo.data.Record.create([
11221     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11222     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11223 ]);
11224 var myReader = new Roo.data.ArrayReader({
11225     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11226 }, RecordDef);
11227 </code></pre>
11228  * <p>
11229  * This would consume an Array like this:
11230  * <pre><code>
11231 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11232   </code></pre>
11233  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11234  * @constructor
11235  * Create a new JsonReader
11236  * @param {Object} meta Metadata configuration options.
11237  * @param {Object} recordType Either an Array of field definition objects
11238  * as specified to {@link Roo.data.Record#create},
11239  * or an {@link Roo.data.Record} object
11240  * created using {@link Roo.data.Record#create}.
11241  */
11242 Roo.data.ArrayReader = function(meta, recordType){
11243     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11244 };
11245
11246 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11247     /**
11248      * Create a data block containing Roo.data.Records from an XML document.
11249      * @param {Object} o An Array of row objects which represents the dataset.
11250      * @return {Object} data A data block which is used by an Roo.data.Store object as
11251      * a cache of Roo.data.Records.
11252      */
11253     readRecords : function(o){
11254         var sid = this.meta ? this.meta.id : null;
11255         var recordType = this.recordType, fields = recordType.prototype.fields;
11256         var records = [];
11257         var root = o;
11258             for(var i = 0; i < root.length; i++){
11259                     var n = root[i];
11260                 var values = {};
11261                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11262                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11263                 var f = fields.items[j];
11264                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11265                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11266                 v = f.convert(v);
11267                 values[f.name] = v;
11268             }
11269                 var record = new recordType(values, id);
11270                 record.json = n;
11271                 records[records.length] = record;
11272             }
11273             return {
11274                 records : records,
11275                 totalRecords : records.length
11276             };
11277     }
11278 });/*
11279  * - LGPL
11280  * * 
11281  */
11282
11283 /**
11284  * @class Roo.bootstrap.ComboBox
11285  * @extends Roo.bootstrap.TriggerField
11286  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11287  * @cfg {Boolean} append (true|false) default false
11288  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11289  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11290  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11291  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11292  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11293  * @cfg {Boolean} animate default true
11294  * @cfg {Boolean} emptyResultText only for touch device
11295  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11296  * @constructor
11297  * Create a new ComboBox.
11298  * @param {Object} config Configuration options
11299  */
11300 Roo.bootstrap.ComboBox = function(config){
11301     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11302     this.addEvents({
11303         /**
11304          * @event expand
11305          * Fires when the dropdown list is expanded
11306              * @param {Roo.bootstrap.ComboBox} combo This combo box
11307              */
11308         'expand' : true,
11309         /**
11310          * @event collapse
11311          * Fires when the dropdown list is collapsed
11312              * @param {Roo.bootstrap.ComboBox} combo This combo box
11313              */
11314         'collapse' : true,
11315         /**
11316          * @event beforeselect
11317          * Fires before a list item is selected. Return false to cancel the selection.
11318              * @param {Roo.bootstrap.ComboBox} combo This combo box
11319              * @param {Roo.data.Record} record The data record returned from the underlying store
11320              * @param {Number} index The index of the selected item in the dropdown list
11321              */
11322         'beforeselect' : true,
11323         /**
11324          * @event select
11325          * Fires when a list item is selected
11326              * @param {Roo.bootstrap.ComboBox} combo This combo box
11327              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11328              * @param {Number} index The index of the selected item in the dropdown list
11329              */
11330         'select' : true,
11331         /**
11332          * @event beforequery
11333          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11334          * The event object passed has these properties:
11335              * @param {Roo.bootstrap.ComboBox} combo This combo box
11336              * @param {String} query The query
11337              * @param {Boolean} forceAll true to force "all" query
11338              * @param {Boolean} cancel true to cancel the query
11339              * @param {Object} e The query event object
11340              */
11341         'beforequery': true,
11342          /**
11343          * @event add
11344          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11345              * @param {Roo.bootstrap.ComboBox} combo This combo box
11346              */
11347         'add' : true,
11348         /**
11349          * @event edit
11350          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11351              * @param {Roo.bootstrap.ComboBox} combo This combo box
11352              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11353              */
11354         'edit' : true,
11355         /**
11356          * @event remove
11357          * Fires when the remove value from the combobox array
11358              * @param {Roo.bootstrap.ComboBox} combo This combo box
11359              */
11360         'remove' : true,
11361         /**
11362          * @event specialfilter
11363          * Fires when specialfilter
11364             * @param {Roo.bootstrap.ComboBox} combo This combo box
11365             */
11366         'specialfilter' : true,
11367         /**
11368          * @event tick
11369          * Fires when tick the element
11370             * @param {Roo.bootstrap.ComboBox} combo This combo box
11371             */
11372         'tick' : true
11373         
11374     });
11375     
11376     this.item = [];
11377     this.tickItems = [];
11378     
11379     this.selectedIndex = -1;
11380     if(this.mode == 'local'){
11381         if(config.queryDelay === undefined){
11382             this.queryDelay = 10;
11383         }
11384         if(config.minChars === undefined){
11385             this.minChars = 0;
11386         }
11387     }
11388 };
11389
11390 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11391      
11392     /**
11393      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11394      * rendering into an Roo.Editor, defaults to false)
11395      */
11396     /**
11397      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11398      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11399      */
11400     /**
11401      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11402      */
11403     /**
11404      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11405      * the dropdown list (defaults to undefined, with no header element)
11406      */
11407
11408      /**
11409      * @cfg {String/Roo.Template} tpl The template to use to render the output
11410      */
11411      
11412      /**
11413      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11414      */
11415     listWidth: undefined,
11416     /**
11417      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11418      * mode = 'remote' or 'text' if mode = 'local')
11419      */
11420     displayField: undefined,
11421     
11422     /**
11423      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11424      * mode = 'remote' or 'value' if mode = 'local'). 
11425      * Note: use of a valueField requires the user make a selection
11426      * in order for a value to be mapped.
11427      */
11428     valueField: undefined,
11429     
11430     
11431     /**
11432      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11433      * field's data value (defaults to the underlying DOM element's name)
11434      */
11435     hiddenName: undefined,
11436     /**
11437      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11438      */
11439     listClass: '',
11440     /**
11441      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11442      */
11443     selectedClass: 'active',
11444     
11445     /**
11446      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11447      */
11448     shadow:'sides',
11449     /**
11450      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11451      * anchor positions (defaults to 'tl-bl')
11452      */
11453     listAlign: 'tl-bl?',
11454     /**
11455      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11456      */
11457     maxHeight: 300,
11458     /**
11459      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11460      * query specified by the allQuery config option (defaults to 'query')
11461      */
11462     triggerAction: 'query',
11463     /**
11464      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11465      * (defaults to 4, does not apply if editable = false)
11466      */
11467     minChars : 4,
11468     /**
11469      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11470      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11471      */
11472     typeAhead: false,
11473     /**
11474      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11475      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11476      */
11477     queryDelay: 500,
11478     /**
11479      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11480      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11481      */
11482     pageSize: 0,
11483     /**
11484      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11485      * when editable = true (defaults to false)
11486      */
11487     selectOnFocus:false,
11488     /**
11489      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11490      */
11491     queryParam: 'query',
11492     /**
11493      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11494      * when mode = 'remote' (defaults to 'Loading...')
11495      */
11496     loadingText: 'Loading...',
11497     /**
11498      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11499      */
11500     resizable: false,
11501     /**
11502      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11503      */
11504     handleHeight : 8,
11505     /**
11506      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11507      * traditional select (defaults to true)
11508      */
11509     editable: true,
11510     /**
11511      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11512      */
11513     allQuery: '',
11514     /**
11515      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11516      */
11517     mode: 'remote',
11518     /**
11519      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11520      * listWidth has a higher value)
11521      */
11522     minListWidth : 70,
11523     /**
11524      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11525      * allow the user to set arbitrary text into the field (defaults to false)
11526      */
11527     forceSelection:false,
11528     /**
11529      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11530      * if typeAhead = true (defaults to 250)
11531      */
11532     typeAheadDelay : 250,
11533     /**
11534      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11535      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11536      */
11537     valueNotFoundText : undefined,
11538     /**
11539      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11540      */
11541     blockFocus : false,
11542     
11543     /**
11544      * @cfg {Boolean} disableClear Disable showing of clear button.
11545      */
11546     disableClear : false,
11547     /**
11548      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11549      */
11550     alwaysQuery : false,
11551     
11552     /**
11553      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11554      */
11555     multiple : false,
11556     
11557     /**
11558      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11559      */
11560     invalidClass : "has-warning",
11561     
11562     /**
11563      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11564      */
11565     validClass : "has-success",
11566     
11567     /**
11568      * @cfg {Boolean} specialFilter (true|false) special filter default false
11569      */
11570     specialFilter : false,
11571     
11572     /**
11573      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11574      */
11575     mobileTouchView : true,
11576     
11577     //private
11578     addicon : false,
11579     editicon: false,
11580     
11581     page: 0,
11582     hasQuery: false,
11583     append: false,
11584     loadNext: false,
11585     autoFocus : true,
11586     tickable : false,
11587     btnPosition : 'right',
11588     triggerList : true,
11589     showToggleBtn : true,
11590     animate : true,
11591     emptyResultText: 'Empty',
11592     triggerText : 'Select',
11593     
11594     // element that contains real text value.. (when hidden is used..)
11595     
11596     getAutoCreate : function()
11597     {
11598         var cfg = false;
11599         
11600         /*
11601          * Touch Devices
11602          */
11603         
11604         if(Roo.isTouch && this.mobileTouchView){
11605             cfg = this.getAutoCreateTouchView();
11606             return cfg;;
11607         }
11608         
11609         /*
11610          *  Normal ComboBox
11611          */
11612         if(!this.tickable){
11613             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11614             return cfg;
11615         }
11616         
11617         /*
11618          *  ComboBox with tickable selections
11619          */
11620              
11621         var align = this.labelAlign || this.parentLabelAlign();
11622         
11623         cfg = {
11624             cls : 'form-group roo-combobox-tickable' //input-group
11625         };
11626         
11627         var buttons = {
11628             tag : 'div',
11629             cls : 'tickable-buttons',
11630             cn : [
11631                 {
11632                     tag : 'button',
11633                     type : 'button',
11634                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11635                     html : this.triggerText
11636                 },
11637                 {
11638                     tag : 'button',
11639                     type : 'button',
11640                     name : 'ok',
11641                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11642                     html : 'Done'
11643                 },
11644                 {
11645                     tag : 'button',
11646                     type : 'button',
11647                     name : 'cancel',
11648                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11649                     html : 'Cancel'
11650                 }
11651             ]
11652         };
11653         
11654         if(this.editable){
11655             buttons.cn.unshift({
11656                 tag: 'input',
11657                 cls: 'select2-search-field-input'
11658             });
11659         }
11660         
11661         var _this = this;
11662         
11663         Roo.each(buttons.cn, function(c){
11664             if (_this.size) {
11665                 c.cls += ' btn-' + _this.size;
11666             }
11667
11668             if (_this.disabled) {
11669                 c.disabled = true;
11670             }
11671         });
11672         
11673         var box = {
11674             tag: 'div',
11675             cn: [
11676                 {
11677                     tag: 'input',
11678                     type : 'hidden',
11679                     cls: 'form-hidden-field'
11680                 },
11681                 {
11682                     tag: 'ul',
11683                     cls: 'select2-choices',
11684                     cn:[
11685                         {
11686                             tag: 'li',
11687                             cls: 'select2-search-field',
11688                             cn: [
11689
11690                                 buttons
11691                             ]
11692                         }
11693                     ]
11694                 }
11695             ]
11696         };
11697         
11698         var combobox = {
11699             cls: 'select2-container input-group select2-container-multi',
11700             cn: [
11701                 box
11702 //                {
11703 //                    tag: 'ul',
11704 //                    cls: 'typeahead typeahead-long dropdown-menu',
11705 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11706 //                }
11707             ]
11708         };
11709         
11710         if(this.hasFeedback && !this.allowBlank){
11711             
11712             var feedback = {
11713                 tag: 'span',
11714                 cls: 'glyphicon form-control-feedback'
11715             };
11716
11717             combobox.cn.push(feedback);
11718         }
11719         
11720         if (align ==='left' && this.fieldLabel.length) {
11721             
11722                 Roo.log("left and has label");
11723                 cfg.cn = [
11724                     
11725                     {
11726                         tag: 'label',
11727                         'for' :  id,
11728                         cls : 'control-label col-sm-' + this.labelWidth,
11729                         html : this.fieldLabel
11730                         
11731                     },
11732                     {
11733                         cls : "col-sm-" + (12 - this.labelWidth), 
11734                         cn: [
11735                             combobox
11736                         ]
11737                     }
11738                     
11739                 ];
11740         } else if ( this.fieldLabel.length) {
11741                 Roo.log(" label");
11742                  cfg.cn = [
11743                    
11744                     {
11745                         tag: 'label',
11746                         //cls : 'input-group-addon',
11747                         html : this.fieldLabel
11748                         
11749                     },
11750                     
11751                     combobox
11752                     
11753                 ];
11754
11755         } else {
11756             
11757                 Roo.log(" no label && no align");
11758                 cfg = combobox
11759                      
11760                 
11761         }
11762          
11763         var settings=this;
11764         ['xs','sm','md','lg'].map(function(size){
11765             if (settings[size]) {
11766                 cfg.cls += ' col-' + size + '-' + settings[size];
11767             }
11768         });
11769         
11770         return cfg;
11771         
11772     },
11773     
11774     _initEventsCalled : false,
11775     
11776     // private
11777     initEvents: function()
11778     {
11779         
11780         if (this._initEventsCalled) { // as we call render... prevent looping...
11781             return;
11782         }
11783         this._initEventsCalled = true;
11784         
11785         if (!this.store) {
11786             throw "can not find store for combo";
11787         }
11788         
11789         this.store = Roo.factory(this.store, Roo.data);
11790         
11791         // if we are building from html. then this element is so complex, that we can not really
11792         // use the rendered HTML.
11793         // so we have to trash and replace the previous code.
11794         if (Roo.XComponent.build_from_html) {
11795             
11796             // remove this element....
11797             var e = this.el.dom, k=0;
11798             while (e ) { e = e.previousSibling;  ++k;}
11799
11800             this.el.remove();
11801             
11802             this.el=false;
11803             this.rendered = false;
11804             
11805             this.render(this.parent().getChildContainer(true), k);
11806             
11807             
11808             
11809         }
11810         
11811         
11812         /*
11813          * Touch Devices
11814          */
11815         
11816         if(Roo.isTouch && this.mobileTouchView){
11817             this.initTouchView();
11818             return;
11819         }
11820         
11821         if(this.tickable){
11822             this.initTickableEvents();
11823             return;
11824         }
11825         
11826         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11827         
11828         if(this.hiddenName){
11829             
11830             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11831             
11832             this.hiddenField.dom.value =
11833                 this.hiddenValue !== undefined ? this.hiddenValue :
11834                 this.value !== undefined ? this.value : '';
11835
11836             // prevent input submission
11837             this.el.dom.removeAttribute('name');
11838             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11839              
11840              
11841         }
11842         //if(Roo.isGecko){
11843         //    this.el.dom.setAttribute('autocomplete', 'off');
11844         //}
11845         
11846         var cls = 'x-combo-list';
11847         
11848         //this.list = new Roo.Layer({
11849         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11850         //});
11851         
11852         var _this = this;
11853         
11854         (function(){
11855             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11856             _this.list.setWidth(lw);
11857         }).defer(100);
11858         
11859         this.list.on('mouseover', this.onViewOver, this);
11860         this.list.on('mousemove', this.onViewMove, this);
11861         
11862         this.list.on('scroll', this.onViewScroll, this);
11863         
11864         /*
11865         this.list.swallowEvent('mousewheel');
11866         this.assetHeight = 0;
11867
11868         if(this.title){
11869             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11870             this.assetHeight += this.header.getHeight();
11871         }
11872
11873         this.innerList = this.list.createChild({cls:cls+'-inner'});
11874         this.innerList.on('mouseover', this.onViewOver, this);
11875         this.innerList.on('mousemove', this.onViewMove, this);
11876         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11877         
11878         if(this.allowBlank && !this.pageSize && !this.disableClear){
11879             this.footer = this.list.createChild({cls:cls+'-ft'});
11880             this.pageTb = new Roo.Toolbar(this.footer);
11881            
11882         }
11883         if(this.pageSize){
11884             this.footer = this.list.createChild({cls:cls+'-ft'});
11885             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11886                     {pageSize: this.pageSize});
11887             
11888         }
11889         
11890         if (this.pageTb && this.allowBlank && !this.disableClear) {
11891             var _this = this;
11892             this.pageTb.add(new Roo.Toolbar.Fill(), {
11893                 cls: 'x-btn-icon x-btn-clear',
11894                 text: '&#160;',
11895                 handler: function()
11896                 {
11897                     _this.collapse();
11898                     _this.clearValue();
11899                     _this.onSelect(false, -1);
11900                 }
11901             });
11902         }
11903         if (this.footer) {
11904             this.assetHeight += this.footer.getHeight();
11905         }
11906         */
11907             
11908         if(!this.tpl){
11909             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11910         }
11911
11912         this.view = new Roo.View(this.list, this.tpl, {
11913             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11914         });
11915         //this.view.wrapEl.setDisplayed(false);
11916         this.view.on('click', this.onViewClick, this);
11917         
11918         
11919         
11920         this.store.on('beforeload', this.onBeforeLoad, this);
11921         this.store.on('load', this.onLoad, this);
11922         this.store.on('loadexception', this.onLoadException, this);
11923         /*
11924         if(this.resizable){
11925             this.resizer = new Roo.Resizable(this.list,  {
11926                pinned:true, handles:'se'
11927             });
11928             this.resizer.on('resize', function(r, w, h){
11929                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11930                 this.listWidth = w;
11931                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11932                 this.restrictHeight();
11933             }, this);
11934             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11935         }
11936         */
11937         if(!this.editable){
11938             this.editable = true;
11939             this.setEditable(false);
11940         }
11941         
11942         /*
11943         
11944         if (typeof(this.events.add.listeners) != 'undefined') {
11945             
11946             this.addicon = this.wrap.createChild(
11947                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11948        
11949             this.addicon.on('click', function(e) {
11950                 this.fireEvent('add', this);
11951             }, this);
11952         }
11953         if (typeof(this.events.edit.listeners) != 'undefined') {
11954             
11955             this.editicon = this.wrap.createChild(
11956                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11957             if (this.addicon) {
11958                 this.editicon.setStyle('margin-left', '40px');
11959             }
11960             this.editicon.on('click', function(e) {
11961                 
11962                 // we fire even  if inothing is selected..
11963                 this.fireEvent('edit', this, this.lastData );
11964                 
11965             }, this);
11966         }
11967         */
11968         
11969         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11970             "up" : function(e){
11971                 this.inKeyMode = true;
11972                 this.selectPrev();
11973             },
11974
11975             "down" : function(e){
11976                 if(!this.isExpanded()){
11977                     this.onTriggerClick();
11978                 }else{
11979                     this.inKeyMode = true;
11980                     this.selectNext();
11981                 }
11982             },
11983
11984             "enter" : function(e){
11985 //                this.onViewClick();
11986                 //return true;
11987                 this.collapse();
11988                 
11989                 if(this.fireEvent("specialkey", this, e)){
11990                     this.onViewClick(false);
11991                 }
11992                 
11993                 return true;
11994             },
11995
11996             "esc" : function(e){
11997                 this.collapse();
11998             },
11999
12000             "tab" : function(e){
12001                 this.collapse();
12002                 
12003                 if(this.fireEvent("specialkey", this, e)){
12004                     this.onViewClick(false);
12005                 }
12006                 
12007                 return true;
12008             },
12009
12010             scope : this,
12011
12012             doRelay : function(foo, bar, hname){
12013                 if(hname == 'down' || this.scope.isExpanded()){
12014                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12015                 }
12016                 return true;
12017             },
12018
12019             forceKeyDown: true
12020         });
12021         
12022         
12023         this.queryDelay = Math.max(this.queryDelay || 10,
12024                 this.mode == 'local' ? 10 : 250);
12025         
12026         
12027         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12028         
12029         if(this.typeAhead){
12030             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12031         }
12032         if(this.editable !== false){
12033             this.inputEl().on("keyup", this.onKeyUp, this);
12034         }
12035         if(this.forceSelection){
12036             this.inputEl().on('blur', this.doForce, this);
12037         }
12038         
12039         if(this.multiple){
12040             this.choices = this.el.select('ul.select2-choices', true).first();
12041             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12042         }
12043     },
12044     
12045     initTickableEvents: function()
12046     {   
12047         this.createList();
12048         
12049         if(this.hiddenName){
12050             
12051             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12052             
12053             this.hiddenField.dom.value =
12054                 this.hiddenValue !== undefined ? this.hiddenValue :
12055                 this.value !== undefined ? this.value : '';
12056
12057             // prevent input submission
12058             this.el.dom.removeAttribute('name');
12059             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12060              
12061              
12062         }
12063         
12064 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12065         
12066         this.choices = this.el.select('ul.select2-choices', true).first();
12067         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12068         if(this.triggerList){
12069             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12070         }
12071          
12072         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12073         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12074         
12075         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12076         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12077         
12078         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12079         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12080         
12081         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12082         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12083         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12084         
12085         this.okBtn.hide();
12086         this.cancelBtn.hide();
12087         
12088         var _this = this;
12089         
12090         (function(){
12091             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12092             _this.list.setWidth(lw);
12093         }).defer(100);
12094         
12095         this.list.on('mouseover', this.onViewOver, this);
12096         this.list.on('mousemove', this.onViewMove, this);
12097         
12098         this.list.on('scroll', this.onViewScroll, this);
12099         
12100         if(!this.tpl){
12101             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>';
12102         }
12103
12104         this.view = new Roo.View(this.list, this.tpl, {
12105             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12106         });
12107         
12108         //this.view.wrapEl.setDisplayed(false);
12109         this.view.on('click', this.onViewClick, this);
12110         
12111         
12112         
12113         this.store.on('beforeload', this.onBeforeLoad, this);
12114         this.store.on('load', this.onLoad, this);
12115         this.store.on('loadexception', this.onLoadException, this);
12116         
12117         if(this.editable){
12118             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12119                 "up" : function(e){
12120                     this.inKeyMode = true;
12121                     this.selectPrev();
12122                 },
12123
12124                 "down" : function(e){
12125                     this.inKeyMode = true;
12126                     this.selectNext();
12127                 },
12128
12129                 "enter" : function(e){
12130                     if(this.fireEvent("specialkey", this, e)){
12131                         this.onViewClick(false);
12132                     }
12133                     
12134                     return true;
12135                 },
12136
12137                 "esc" : function(e){
12138                     this.onTickableFooterButtonClick(e, false, false);
12139                 },
12140
12141                 "tab" : function(e){
12142                     this.fireEvent("specialkey", this, e);
12143                     
12144                     this.onTickableFooterButtonClick(e, false, false);
12145                     
12146                     return true;
12147                 },
12148
12149                 scope : this,
12150
12151                 doRelay : function(e, fn, key){
12152                     if(this.scope.isExpanded()){
12153                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12154                     }
12155                     return true;
12156                 },
12157
12158                 forceKeyDown: true
12159             });
12160         }
12161         
12162         this.queryDelay = Math.max(this.queryDelay || 10,
12163                 this.mode == 'local' ? 10 : 250);
12164         
12165         
12166         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12167         
12168         if(this.typeAhead){
12169             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12170         }
12171         
12172         if(this.editable !== false){
12173             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12174         }
12175         
12176     },
12177
12178     onDestroy : function(){
12179         if(this.view){
12180             this.view.setStore(null);
12181             this.view.el.removeAllListeners();
12182             this.view.el.remove();
12183             this.view.purgeListeners();
12184         }
12185         if(this.list){
12186             this.list.dom.innerHTML  = '';
12187         }
12188         
12189         if(this.store){
12190             this.store.un('beforeload', this.onBeforeLoad, this);
12191             this.store.un('load', this.onLoad, this);
12192             this.store.un('loadexception', this.onLoadException, this);
12193         }
12194         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12195     },
12196
12197     // private
12198     fireKey : function(e){
12199         if(e.isNavKeyPress() && !this.list.isVisible()){
12200             this.fireEvent("specialkey", this, e);
12201         }
12202     },
12203
12204     // private
12205     onResize: function(w, h){
12206 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12207 //        
12208 //        if(typeof w != 'number'){
12209 //            // we do not handle it!?!?
12210 //            return;
12211 //        }
12212 //        var tw = this.trigger.getWidth();
12213 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12214 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12215 //        var x = w - tw;
12216 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12217 //            
12218 //        //this.trigger.setStyle('left', x+'px');
12219 //        
12220 //        if(this.list && this.listWidth === undefined){
12221 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12222 //            this.list.setWidth(lw);
12223 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12224 //        }
12225         
12226     
12227         
12228     },
12229
12230     /**
12231      * Allow or prevent the user from directly editing the field text.  If false is passed,
12232      * the user will only be able to select from the items defined in the dropdown list.  This method
12233      * is the runtime equivalent of setting the 'editable' config option at config time.
12234      * @param {Boolean} value True to allow the user to directly edit the field text
12235      */
12236     setEditable : function(value){
12237         if(value == this.editable){
12238             return;
12239         }
12240         this.editable = value;
12241         if(!value){
12242             this.inputEl().dom.setAttribute('readOnly', true);
12243             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12244             this.inputEl().addClass('x-combo-noedit');
12245         }else{
12246             this.inputEl().dom.setAttribute('readOnly', false);
12247             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12248             this.inputEl().removeClass('x-combo-noedit');
12249         }
12250     },
12251
12252     // private
12253     
12254     onBeforeLoad : function(combo,opts){
12255         if(!this.hasFocus){
12256             return;
12257         }
12258          if (!opts.add) {
12259             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12260          }
12261         this.restrictHeight();
12262         this.selectedIndex = -1;
12263     },
12264
12265     // private
12266     onLoad : function(){
12267         
12268         this.hasQuery = false;
12269         
12270         if(!this.hasFocus){
12271             return;
12272         }
12273         
12274         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12275             this.loading.hide();
12276         }
12277              
12278         if(this.store.getCount() > 0){
12279             this.expand();
12280             this.restrictHeight();
12281             if(this.lastQuery == this.allQuery){
12282                 if(this.editable && !this.tickable){
12283                     this.inputEl().dom.select();
12284                 }
12285                 
12286                 if(
12287                     !this.selectByValue(this.value, true) &&
12288                     this.autoFocus && 
12289                     (
12290                         !this.store.lastOptions ||
12291                         typeof(this.store.lastOptions.add) == 'undefined' || 
12292                         this.store.lastOptions.add != true
12293                     )
12294                 ){
12295                     this.select(0, true);
12296                 }
12297             }else{
12298                 if(this.autoFocus){
12299                     this.selectNext();
12300                 }
12301                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12302                     this.taTask.delay(this.typeAheadDelay);
12303                 }
12304             }
12305         }else{
12306             this.onEmptyResults();
12307         }
12308         
12309         //this.el.focus();
12310     },
12311     // private
12312     onLoadException : function()
12313     {
12314         this.hasQuery = false;
12315         
12316         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12317             this.loading.hide();
12318         }
12319         
12320         if(this.tickable && this.editable){
12321             return;
12322         }
12323         
12324         this.collapse();
12325         // only causes errors at present
12326         //Roo.log(this.store.reader.jsonData);
12327         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12328             // fixme
12329             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12330         //}
12331         
12332         
12333     },
12334     // private
12335     onTypeAhead : function(){
12336         if(this.store.getCount() > 0){
12337             var r = this.store.getAt(0);
12338             var newValue = r.data[this.displayField];
12339             var len = newValue.length;
12340             var selStart = this.getRawValue().length;
12341             
12342             if(selStart != len){
12343                 this.setRawValue(newValue);
12344                 this.selectText(selStart, newValue.length);
12345             }
12346         }
12347     },
12348
12349     // private
12350     onSelect : function(record, index){
12351         
12352         if(this.fireEvent('beforeselect', this, record, index) !== false){
12353         
12354             this.setFromData(index > -1 ? record.data : false);
12355             
12356             this.collapse();
12357             this.fireEvent('select', this, record, index);
12358         }
12359     },
12360
12361     /**
12362      * Returns the currently selected field value or empty string if no value is set.
12363      * @return {String} value The selected value
12364      */
12365     getValue : function(){
12366         
12367         if(this.multiple){
12368             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12369         }
12370         
12371         if(this.valueField){
12372             return typeof this.value != 'undefined' ? this.value : '';
12373         }else{
12374             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12375         }
12376     },
12377
12378     /**
12379      * Clears any text/value currently set in the field
12380      */
12381     clearValue : function(){
12382         if(this.hiddenField){
12383             this.hiddenField.dom.value = '';
12384         }
12385         this.value = '';
12386         this.setRawValue('');
12387         this.lastSelectionText = '';
12388         this.lastData = false;
12389         
12390         var close = this.closeTriggerEl();
12391         
12392         if(close){
12393             close.hide();
12394         }
12395         
12396     },
12397
12398     /**
12399      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12400      * will be displayed in the field.  If the value does not match the data value of an existing item,
12401      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12402      * Otherwise the field will be blank (although the value will still be set).
12403      * @param {String} value The value to match
12404      */
12405     setValue : function(v){
12406         if(this.multiple){
12407             this.syncValue();
12408             return;
12409         }
12410         
12411         var text = v;
12412         if(this.valueField){
12413             var r = this.findRecord(this.valueField, v);
12414             if(r){
12415                 text = r.data[this.displayField];
12416             }else if(this.valueNotFoundText !== undefined){
12417                 text = this.valueNotFoundText;
12418             }
12419         }
12420         this.lastSelectionText = text;
12421         if(this.hiddenField){
12422             this.hiddenField.dom.value = v;
12423         }
12424         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12425         this.value = v;
12426         
12427         var close = this.closeTriggerEl();
12428         
12429         if(close){
12430             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12431         }
12432     },
12433     /**
12434      * @property {Object} the last set data for the element
12435      */
12436     
12437     lastData : false,
12438     /**
12439      * Sets the value of the field based on a object which is related to the record format for the store.
12440      * @param {Object} value the value to set as. or false on reset?
12441      */
12442     setFromData : function(o){
12443         
12444         if(this.multiple){
12445             this.addItem(o);
12446             return;
12447         }
12448             
12449         var dv = ''; // display value
12450         var vv = ''; // value value..
12451         this.lastData = o;
12452         if (this.displayField) {
12453             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12454         } else {
12455             // this is an error condition!!!
12456             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12457         }
12458         
12459         if(this.valueField){
12460             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12461         }
12462         
12463         var close = this.closeTriggerEl();
12464         
12465         if(close){
12466             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12467         }
12468         
12469         if(this.hiddenField){
12470             this.hiddenField.dom.value = vv;
12471             
12472             this.lastSelectionText = dv;
12473             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12474             this.value = vv;
12475             return;
12476         }
12477         // no hidden field.. - we store the value in 'value', but still display
12478         // display field!!!!
12479         this.lastSelectionText = dv;
12480         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12481         this.value = vv;
12482         
12483         
12484         
12485     },
12486     // private
12487     reset : function(){
12488         // overridden so that last data is reset..
12489         
12490         if(this.multiple){
12491             this.clearItem();
12492             return;
12493         }
12494         
12495         this.setValue(this.originalValue);
12496         this.clearInvalid();
12497         this.lastData = false;
12498         if (this.view) {
12499             this.view.clearSelections();
12500         }
12501     },
12502     // private
12503     findRecord : function(prop, value){
12504         var record;
12505         if(this.store.getCount() > 0){
12506             this.store.each(function(r){
12507                 if(r.data[prop] == value){
12508                     record = r;
12509                     return false;
12510                 }
12511                 return true;
12512             });
12513         }
12514         return record;
12515     },
12516     
12517     getName: function()
12518     {
12519         // returns hidden if it's set..
12520         if (!this.rendered) {return ''};
12521         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12522         
12523     },
12524     // private
12525     onViewMove : function(e, t){
12526         this.inKeyMode = false;
12527     },
12528
12529     // private
12530     onViewOver : function(e, t){
12531         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12532             return;
12533         }
12534         var item = this.view.findItemFromChild(t);
12535         
12536         if(item){
12537             var index = this.view.indexOf(item);
12538             this.select(index, false);
12539         }
12540     },
12541
12542     // private
12543     onViewClick : function(view, doFocus, el, e)
12544     {
12545         var index = this.view.getSelectedIndexes()[0];
12546         
12547         var r = this.store.getAt(index);
12548         
12549         if(this.tickable){
12550             
12551             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12552                 return;
12553             }
12554             
12555             var rm = false;
12556             var _this = this;
12557             
12558             Roo.each(this.tickItems, function(v,k){
12559                 
12560                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12561                     Roo.log(v);
12562                     _this.tickItems.splice(k, 1);
12563                     
12564                     if(typeof(e) == 'undefined' && view == false){
12565                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12566                     }
12567                     
12568                     rm = true;
12569                     return;
12570                 }
12571             });
12572             
12573             if(rm){
12574                 return;
12575             }
12576             
12577             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12578                 this.tickItems.push(r.data);
12579             }
12580             
12581             if(typeof(e) == 'undefined' && view == false){
12582                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12583             }
12584                     
12585             return;
12586         }
12587         
12588         if(r){
12589             this.onSelect(r, index);
12590         }
12591         if(doFocus !== false && !this.blockFocus){
12592             this.inputEl().focus();
12593         }
12594     },
12595
12596     // private
12597     restrictHeight : function(){
12598         //this.innerList.dom.style.height = '';
12599         //var inner = this.innerList.dom;
12600         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12601         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12602         //this.list.beginUpdate();
12603         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12604         this.list.alignTo(this.inputEl(), this.listAlign);
12605         this.list.alignTo(this.inputEl(), this.listAlign);
12606         //this.list.endUpdate();
12607     },
12608
12609     // private
12610     onEmptyResults : function(){
12611         
12612         if(this.tickable && this.editable){
12613             this.restrictHeight();
12614             return;
12615         }
12616         
12617         this.collapse();
12618     },
12619
12620     /**
12621      * Returns true if the dropdown list is expanded, else false.
12622      */
12623     isExpanded : function(){
12624         return this.list.isVisible();
12625     },
12626
12627     /**
12628      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12629      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12630      * @param {String} value The data value of the item to select
12631      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12632      * selected item if it is not currently in view (defaults to true)
12633      * @return {Boolean} True if the value matched an item in the list, else false
12634      */
12635     selectByValue : function(v, scrollIntoView){
12636         if(v !== undefined && v !== null){
12637             var r = this.findRecord(this.valueField || this.displayField, v);
12638             if(r){
12639                 this.select(this.store.indexOf(r), scrollIntoView);
12640                 return true;
12641             }
12642         }
12643         return false;
12644     },
12645
12646     /**
12647      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12648      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12649      * @param {Number} index The zero-based index of the list item to select
12650      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12651      * selected item if it is not currently in view (defaults to true)
12652      */
12653     select : function(index, scrollIntoView){
12654         this.selectedIndex = index;
12655         this.view.select(index);
12656         if(scrollIntoView !== false){
12657             var el = this.view.getNode(index);
12658             /*
12659              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12660              */
12661             if(el){
12662                 this.list.scrollChildIntoView(el, false);
12663             }
12664         }
12665     },
12666
12667     // private
12668     selectNext : function(){
12669         var ct = this.store.getCount();
12670         if(ct > 0){
12671             if(this.selectedIndex == -1){
12672                 this.select(0);
12673             }else if(this.selectedIndex < ct-1){
12674                 this.select(this.selectedIndex+1);
12675             }
12676         }
12677     },
12678
12679     // private
12680     selectPrev : function(){
12681         var ct = this.store.getCount();
12682         if(ct > 0){
12683             if(this.selectedIndex == -1){
12684                 this.select(0);
12685             }else if(this.selectedIndex != 0){
12686                 this.select(this.selectedIndex-1);
12687             }
12688         }
12689     },
12690
12691     // private
12692     onKeyUp : function(e){
12693         if(this.editable !== false && !e.isSpecialKey()){
12694             this.lastKey = e.getKey();
12695             this.dqTask.delay(this.queryDelay);
12696         }
12697     },
12698
12699     // private
12700     validateBlur : function(){
12701         return !this.list || !this.list.isVisible();   
12702     },
12703
12704     // private
12705     initQuery : function(){
12706         
12707         var v = this.getRawValue();
12708         
12709         if(this.tickable && this.editable){
12710             v = this.tickableInputEl().getValue();
12711         }
12712         
12713         this.doQuery(v);
12714     },
12715
12716     // private
12717     doForce : function(){
12718         if(this.inputEl().dom.value.length > 0){
12719             this.inputEl().dom.value =
12720                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12721              
12722         }
12723     },
12724
12725     /**
12726      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12727      * query allowing the query action to be canceled if needed.
12728      * @param {String} query The SQL query to execute
12729      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12730      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12731      * saved in the current store (defaults to false)
12732      */
12733     doQuery : function(q, forceAll){
12734         
12735         if(q === undefined || q === null){
12736             q = '';
12737         }
12738         var qe = {
12739             query: q,
12740             forceAll: forceAll,
12741             combo: this,
12742             cancel:false
12743         };
12744         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12745             return false;
12746         }
12747         q = qe.query;
12748         
12749         forceAll = qe.forceAll;
12750         if(forceAll === true || (q.length >= this.minChars)){
12751             
12752             this.hasQuery = true;
12753             
12754             if(this.lastQuery != q || this.alwaysQuery){
12755                 this.lastQuery = q;
12756                 if(this.mode == 'local'){
12757                     this.selectedIndex = -1;
12758                     if(forceAll){
12759                         this.store.clearFilter();
12760                     }else{
12761                         
12762                         if(this.specialFilter){
12763                             this.fireEvent('specialfilter', this);
12764                             this.onLoad();
12765                             return;
12766                         }
12767                         
12768                         this.store.filter(this.displayField, q);
12769                     }
12770                     
12771                     this.store.fireEvent("datachanged", this.store);
12772                     
12773                     this.onLoad();
12774                     
12775                     
12776                 }else{
12777                     
12778                     this.store.baseParams[this.queryParam] = q;
12779                     
12780                     var options = {params : this.getParams(q)};
12781                     
12782                     if(this.loadNext){
12783                         options.add = true;
12784                         options.params.start = this.page * this.pageSize;
12785                     }
12786                     
12787                     this.store.load(options);
12788                     
12789                     /*
12790                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12791                      *  we should expand the list on onLoad
12792                      *  so command out it
12793                      */
12794 //                    this.expand();
12795                 }
12796             }else{
12797                 this.selectedIndex = -1;
12798                 this.onLoad();   
12799             }
12800         }
12801         
12802         this.loadNext = false;
12803     },
12804     
12805     // private
12806     getParams : function(q){
12807         var p = {};
12808         //p[this.queryParam] = q;
12809         
12810         if(this.pageSize){
12811             p.start = 0;
12812             p.limit = this.pageSize;
12813         }
12814         return p;
12815     },
12816
12817     /**
12818      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12819      */
12820     collapse : function(){
12821         if(!this.isExpanded()){
12822             return;
12823         }
12824         
12825         this.list.hide();
12826         
12827         if(this.tickable){
12828             this.hasFocus = false;
12829             this.okBtn.hide();
12830             this.cancelBtn.hide();
12831             this.trigger.show();
12832             
12833             if(this.editable){
12834                 this.tickableInputEl().dom.value = '';
12835                 this.tickableInputEl().blur();
12836             }
12837             
12838         }
12839         
12840         Roo.get(document).un('mousedown', this.collapseIf, this);
12841         Roo.get(document).un('mousewheel', this.collapseIf, this);
12842         if (!this.editable) {
12843             Roo.get(document).un('keydown', this.listKeyPress, this);
12844         }
12845         this.fireEvent('collapse', this);
12846     },
12847
12848     // private
12849     collapseIf : function(e){
12850         var in_combo  = e.within(this.el);
12851         var in_list =  e.within(this.list);
12852         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12853         
12854         if (in_combo || in_list || is_list) {
12855             //e.stopPropagation();
12856             return;
12857         }
12858         
12859         if(this.tickable){
12860             this.onTickableFooterButtonClick(e, false, false);
12861         }
12862
12863         this.collapse();
12864         
12865     },
12866
12867     /**
12868      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12869      */
12870     expand : function(){
12871        
12872         if(this.isExpanded() || !this.hasFocus){
12873             return;
12874         }
12875         
12876         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12877         this.list.setWidth(lw);
12878         
12879         
12880          Roo.log('expand');
12881         
12882         this.list.show();
12883         
12884         this.restrictHeight();
12885         
12886         if(this.tickable){
12887             
12888             this.tickItems = Roo.apply([], this.item);
12889             
12890             this.okBtn.show();
12891             this.cancelBtn.show();
12892             this.trigger.hide();
12893             
12894             if(this.editable){
12895                 this.tickableInputEl().focus();
12896             }
12897             
12898         }
12899         
12900         Roo.get(document).on('mousedown', this.collapseIf, this);
12901         Roo.get(document).on('mousewheel', this.collapseIf, this);
12902         if (!this.editable) {
12903             Roo.get(document).on('keydown', this.listKeyPress, this);
12904         }
12905         
12906         this.fireEvent('expand', this);
12907     },
12908
12909     // private
12910     // Implements the default empty TriggerField.onTriggerClick function
12911     onTriggerClick : function(e)
12912     {
12913         Roo.log('trigger click');
12914         
12915         if(this.disabled || !this.triggerList){
12916             return;
12917         }
12918         
12919         this.page = 0;
12920         this.loadNext = false;
12921         
12922         if(this.isExpanded()){
12923             this.collapse();
12924             if (!this.blockFocus) {
12925                 this.inputEl().focus();
12926             }
12927             
12928         }else {
12929             this.hasFocus = true;
12930             if(this.triggerAction == 'all') {
12931                 this.doQuery(this.allQuery, true);
12932             } else {
12933                 this.doQuery(this.getRawValue());
12934             }
12935             if (!this.blockFocus) {
12936                 this.inputEl().focus();
12937             }
12938         }
12939     },
12940     
12941     onTickableTriggerClick : function(e)
12942     {
12943         if(this.disabled){
12944             return;
12945         }
12946         
12947         this.page = 0;
12948         this.loadNext = false;
12949         this.hasFocus = true;
12950         
12951         if(this.triggerAction == 'all') {
12952             this.doQuery(this.allQuery, true);
12953         } else {
12954             this.doQuery(this.getRawValue());
12955         }
12956     },
12957     
12958     onSearchFieldClick : function(e)
12959     {
12960         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12961             this.onTickableFooterButtonClick(e, false, false);
12962             return;
12963         }
12964         
12965         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12966             return;
12967         }
12968         
12969         this.page = 0;
12970         this.loadNext = false;
12971         this.hasFocus = true;
12972         
12973         if(this.triggerAction == 'all') {
12974             this.doQuery(this.allQuery, true);
12975         } else {
12976             this.doQuery(this.getRawValue());
12977         }
12978     },
12979     
12980     listKeyPress : function(e)
12981     {
12982         //Roo.log('listkeypress');
12983         // scroll to first matching element based on key pres..
12984         if (e.isSpecialKey()) {
12985             return false;
12986         }
12987         var k = String.fromCharCode(e.getKey()).toUpperCase();
12988         //Roo.log(k);
12989         var match  = false;
12990         var csel = this.view.getSelectedNodes();
12991         var cselitem = false;
12992         if (csel.length) {
12993             var ix = this.view.indexOf(csel[0]);
12994             cselitem  = this.store.getAt(ix);
12995             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12996                 cselitem = false;
12997             }
12998             
12999         }
13000         
13001         this.store.each(function(v) { 
13002             if (cselitem) {
13003                 // start at existing selection.
13004                 if (cselitem.id == v.id) {
13005                     cselitem = false;
13006                 }
13007                 return true;
13008             }
13009                 
13010             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13011                 match = this.store.indexOf(v);
13012                 return false;
13013             }
13014             return true;
13015         }, this);
13016         
13017         if (match === false) {
13018             return true; // no more action?
13019         }
13020         // scroll to?
13021         this.view.select(match);
13022         var sn = Roo.get(this.view.getSelectedNodes()[0])
13023         sn.scrollIntoView(sn.dom.parentNode, false);
13024     },
13025     
13026     onViewScroll : function(e, t){
13027         
13028         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){
13029             return;
13030         }
13031         
13032         this.hasQuery = true;
13033         
13034         this.loading = this.list.select('.loading', true).first();
13035         
13036         if(this.loading === null){
13037             this.list.createChild({
13038                 tag: 'div',
13039                 cls: 'loading select2-more-results select2-active',
13040                 html: 'Loading more results...'
13041             })
13042             
13043             this.loading = this.list.select('.loading', true).first();
13044             
13045             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13046             
13047             this.loading.hide();
13048         }
13049         
13050         this.loading.show();
13051         
13052         var _combo = this;
13053         
13054         this.page++;
13055         this.loadNext = true;
13056         
13057         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13058         
13059         return;
13060     },
13061     
13062     addItem : function(o)
13063     {   
13064         var dv = ''; // display value
13065         
13066         if (this.displayField) {
13067             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13068         } else {
13069             // this is an error condition!!!
13070             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13071         }
13072         
13073         if(!dv.length){
13074             return;
13075         }
13076         
13077         var choice = this.choices.createChild({
13078             tag: 'li',
13079             cls: 'select2-search-choice',
13080             cn: [
13081                 {
13082                     tag: 'div',
13083                     html: dv
13084                 },
13085                 {
13086                     tag: 'a',
13087                     href: '#',
13088                     cls: 'select2-search-choice-close',
13089                     tabindex: '-1'
13090                 }
13091             ]
13092             
13093         }, this.searchField);
13094         
13095         var close = choice.select('a.select2-search-choice-close', true).first()
13096         
13097         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13098         
13099         this.item.push(o);
13100         
13101         this.lastData = o;
13102         
13103         this.syncValue();
13104         
13105         this.inputEl().dom.value = '';
13106         
13107         this.validate();
13108     },
13109     
13110     onRemoveItem : function(e, _self, o)
13111     {
13112         e.preventDefault();
13113         
13114         this.lastItem = Roo.apply([], this.item);
13115         
13116         var index = this.item.indexOf(o.data) * 1;
13117         
13118         if( index < 0){
13119             Roo.log('not this item?!');
13120             return;
13121         }
13122         
13123         this.item.splice(index, 1);
13124         o.item.remove();
13125         
13126         this.syncValue();
13127         
13128         this.fireEvent('remove', this, e);
13129         
13130         this.validate();
13131         
13132     },
13133     
13134     syncValue : function()
13135     {
13136         if(!this.item.length){
13137             this.clearValue();
13138             return;
13139         }
13140             
13141         var value = [];
13142         var _this = this;
13143         Roo.each(this.item, function(i){
13144             if(_this.valueField){
13145                 value.push(i[_this.valueField]);
13146                 return;
13147             }
13148
13149             value.push(i);
13150         });
13151
13152         this.value = value.join(',');
13153
13154         if(this.hiddenField){
13155             this.hiddenField.dom.value = this.value;
13156         }
13157         
13158         this.store.fireEvent("datachanged", this.store);
13159     },
13160     
13161     clearItem : function()
13162     {
13163         if(!this.multiple){
13164             return;
13165         }
13166         
13167         this.item = [];
13168         
13169         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13170            c.remove();
13171         });
13172         
13173         this.syncValue();
13174         
13175         this.validate();
13176         
13177         if(this.tickable && !Roo.isTouch){
13178             this.view.refresh();
13179         }
13180     },
13181     
13182     inputEl: function ()
13183     {
13184         if(Roo.isTouch && this.mobileTouchView){
13185             return this.el.select('input.form-control',true).first();
13186         }
13187         
13188         if(this.tickable){
13189             return this.searchField;
13190         }
13191         
13192         return this.el.select('input.form-control',true).first();
13193     },
13194     
13195     
13196     onTickableFooterButtonClick : function(e, btn, el)
13197     {
13198         e.preventDefault();
13199         
13200         this.lastItem = Roo.apply([], this.item);
13201         
13202         if(btn && btn.name == 'cancel'){
13203             this.tickItems = Roo.apply([], this.item);
13204             this.collapse();
13205             return;
13206         }
13207         
13208         this.clearItem();
13209         
13210         var _this = this;
13211         
13212         Roo.each(this.tickItems, function(o){
13213             _this.addItem(o);
13214         });
13215         
13216         this.collapse();
13217         
13218     },
13219     
13220     validate : function()
13221     {
13222         var v = this.getRawValue();
13223         
13224         if(this.multiple){
13225             v = this.getValue();
13226         }
13227         
13228         if(this.disabled || this.allowBlank || v.length){
13229             this.markValid();
13230             return true;
13231         }
13232         
13233         this.markInvalid();
13234         return false;
13235     },
13236     
13237     tickableInputEl : function()
13238     {
13239         if(!this.tickable || !this.editable){
13240             return this.inputEl();
13241         }
13242         
13243         return this.inputEl().select('.select2-search-field-input', true).first();
13244     },
13245     
13246     
13247     getAutoCreateTouchView : function()
13248     {
13249         var id = Roo.id();
13250         
13251         var cfg = {
13252             cls: 'form-group' //input-group
13253         };
13254         
13255         var input =  {
13256             tag: 'input',
13257             id : id,
13258             type : this.inputType,
13259             cls : 'form-control x-combo-noedit',
13260             autocomplete: 'new-password',
13261             placeholder : this.placeholder || '',
13262             readonly : true
13263         };
13264         
13265         if (this.name) {
13266             input.name = this.name;
13267         }
13268         
13269         if (this.size) {
13270             input.cls += ' input-' + this.size;
13271         }
13272         
13273         if (this.disabled) {
13274             input.disabled = true;
13275         }
13276         
13277         var inputblock = {
13278             cls : '',
13279             cn : [
13280                 input
13281             ]
13282         };
13283         
13284         if(this.before){
13285             inputblock.cls += ' input-group';
13286             
13287             inputblock.cn.unshift({
13288                 tag :'span',
13289                 cls : 'input-group-addon',
13290                 html : this.before
13291             });
13292         }
13293         
13294         if(this.removable && !this.multiple){
13295             inputblock.cls += ' roo-removable';
13296             
13297             inputblock.cn.push({
13298                 tag: 'button',
13299                 html : 'x',
13300                 cls : 'roo-combo-removable-btn close'
13301             });
13302         }
13303
13304         if(this.hasFeedback && !this.allowBlank){
13305             
13306             inputblock.cls += ' has-feedback';
13307             
13308             inputblock.cn.push({
13309                 tag: 'span',
13310                 cls: 'glyphicon form-control-feedback'
13311             });
13312             
13313         }
13314         
13315         if (this.after) {
13316             
13317             inputblock.cls += (this.before) ? '' : ' input-group';
13318             
13319             inputblock.cn.push({
13320                 tag :'span',
13321                 cls : 'input-group-addon',
13322                 html : this.after
13323             });
13324         }
13325
13326         var box = {
13327             tag: 'div',
13328             cn: [
13329                 {
13330                     tag: 'input',
13331                     type : 'hidden',
13332                     cls: 'form-hidden-field'
13333                 },
13334                 inputblock
13335             ]
13336             
13337         };
13338         
13339         if(this.multiple){
13340             box = {
13341                 tag: 'div',
13342                 cn: [
13343                     {
13344                         tag: 'input',
13345                         type : 'hidden',
13346                         cls: 'form-hidden-field'
13347                     },
13348                     {
13349                         tag: 'ul',
13350                         cls: 'select2-choices',
13351                         cn:[
13352                             {
13353                                 tag: 'li',
13354                                 cls: 'select2-search-field',
13355                                 cn: [
13356
13357                                     inputblock
13358                                 ]
13359                             }
13360                         ]
13361                     }
13362                 ]
13363             }
13364         };
13365         
13366         var combobox = {
13367             cls: 'select2-container input-group',
13368             cn: [
13369                 box
13370             ]
13371         };
13372         
13373         if(this.multiple){
13374             combobox.cls += ' select2-container-multi';
13375         }
13376         
13377         var align = this.labelAlign || this.parentLabelAlign();
13378         
13379         cfg.cn = combobox;
13380         
13381         if(this.fieldLabel.length){
13382             
13383             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13384             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13385             
13386             cfg.cn = [
13387                 {
13388                     tag: 'label',
13389                     cls : 'control-label ' + lw,
13390                     html : this.fieldLabel
13391
13392                 },
13393                 {
13394                     cls : cw, 
13395                     cn: [
13396                         combobox
13397                     ]
13398                 }
13399             ];
13400         }
13401         
13402         var settings = this;
13403         
13404         ['xs','sm','md','lg'].map(function(size){
13405             if (settings[size]) {
13406                 cfg.cls += ' col-' + size + '-' + settings[size];
13407             }
13408         });
13409         
13410         return cfg;
13411     },
13412     
13413     initTouchView : function()
13414     {
13415         this.renderTouchView();
13416         
13417         this.touchViewEl.on('scroll', function(){
13418             this.el.dom.scrollTop = 0;
13419         }, this);
13420         
13421         this.inputEl().on("click", this.showTouchView, this);
13422         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13423         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13424         
13425         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13426         
13427         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13428         this.store.on('load', this.onTouchViewLoad, this);
13429         this.store.on('loadexception', this.onTouchViewLoadException, this);
13430         
13431         if(this.hiddenName){
13432             
13433             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13434             
13435             this.hiddenField.dom.value =
13436                 this.hiddenValue !== undefined ? this.hiddenValue :
13437                 this.value !== undefined ? this.value : '';
13438         
13439             this.el.dom.removeAttribute('name');
13440             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13441         }
13442         
13443         if(this.multiple){
13444             this.choices = this.el.select('ul.select2-choices', true).first();
13445             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13446         }
13447         
13448         if(this.removable && !this.multiple){
13449             var close = this.closeTriggerEl();
13450             if(close){
13451                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13452                 close.on('click', this.removeBtnClick, this, close);
13453             }
13454         }
13455         
13456         return;
13457         
13458         
13459     },
13460     
13461     renderTouchView : function()
13462     {
13463         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13464         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13465         
13466         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13467         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13468         
13469         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13470         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13471         this.touchViewBodyEl.setStyle('overflow', 'auto');
13472         
13473         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13474         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13475         
13476         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13477         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13478         
13479     },
13480     
13481     showTouchView : function()
13482     {
13483         this.touchViewHeaderEl.hide();
13484
13485         if(this.fieldLabel.length){
13486             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13487             this.touchViewHeaderEl.show();
13488         }
13489
13490         this.touchViewEl.show();
13491
13492         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13493         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13494
13495         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13496
13497         if(this.fieldLabel.length){
13498             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13499         }
13500         
13501         this.touchViewBodyEl.setHeight(bodyHeight);
13502
13503         if(this.animate){
13504             var _this = this;
13505             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13506         }else{
13507             this.touchViewEl.addClass('in');
13508         }
13509
13510         this.doTouchViewQuery();
13511         
13512     },
13513     
13514     hideTouchView : function()
13515     {
13516         this.touchViewEl.removeClass('in');
13517
13518         if(this.animate){
13519             var _this = this;
13520             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13521         }else{
13522             this.touchViewEl.setStyle('display', 'none');
13523         }
13524         
13525     },
13526     
13527     setTouchViewValue : function()
13528     {
13529         if(this.multiple){
13530             this.clearItem();
13531         
13532             var _this = this;
13533
13534             Roo.each(this.tickItems, function(o){
13535                 this.addItem(o);
13536             }, this);
13537         }
13538         
13539         this.hideTouchView();
13540     },
13541     
13542     doTouchViewQuery : function()
13543     {
13544         var qe = {
13545             query: '',
13546             forceAll: true,
13547             combo: this,
13548             cancel:false
13549         };
13550         
13551         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13552             return false;
13553         }
13554         
13555         if(!this.alwaysQuery || this.mode == 'local'){
13556             this.onTouchViewLoad();
13557             return;
13558         }
13559         
13560         this.store.load();
13561     },
13562     
13563     onTouchViewBeforeLoad : function(combo,opts)
13564     {
13565         return;
13566     },
13567
13568     // private
13569     onTouchViewLoad : function()
13570     {
13571         if(this.store.getCount() < 1){
13572             this.onTouchViewEmptyResults();
13573             return;
13574         }
13575         
13576         this.clearTouchView();
13577         
13578         var rawValue = this.getRawValue();
13579         
13580         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13581         
13582         this.tickItems = [];
13583         
13584         this.store.data.each(function(d, rowIndex){
13585             var row = this.touchViewListGroup.createChild(template);
13586             
13587             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13588                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13589             }
13590             
13591             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13592                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13593             }
13594             
13595             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13596                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13597                 this.tickItems.push(d.data);
13598             }
13599             
13600             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13601             
13602         }, this);
13603         
13604         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13605         
13606         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13607
13608         if(this.fieldLabel.length){
13609             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13610         }
13611
13612         var listHeight = this.touchViewListGroup.getHeight();
13613         
13614         var _this = this;
13615         
13616         if(firstChecked && listHeight > bodyHeight){
13617             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13618         }
13619         
13620     },
13621     
13622     onTouchViewLoadException : function()
13623     {
13624         this.hideTouchView();
13625     },
13626     
13627     onTouchViewEmptyResults : function()
13628     {
13629         this.clearTouchView();
13630         
13631         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13632         
13633         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13634         
13635     },
13636     
13637     clearTouchView : function()
13638     {
13639         this.touchViewListGroup.dom.innerHTML = '';
13640     },
13641     
13642     onTouchViewClick : function(e, el, o)
13643     {
13644         e.preventDefault();
13645         
13646         var row = o.row;
13647         var rowIndex = o.rowIndex;
13648         
13649         var r = this.store.getAt(rowIndex);
13650         
13651         if(!this.multiple){
13652             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13653                 c.dom.removeAttribute('checked');
13654             }, this);
13655             
13656             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13657         
13658             this.setFromData(r.data);
13659             
13660             var close = this.closeTriggerEl();
13661         
13662             if(close){
13663                 close.show();
13664             }
13665
13666             this.hideTouchView();
13667             
13668             this.fireEvent('select', this, r, rowIndex);
13669             
13670             return;
13671         }
13672         
13673         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13674             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13675             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13676             return;
13677         }
13678         
13679         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13680         this.addItem(r.data);
13681         this.tickItems.push(r.data);
13682         
13683     }
13684     
13685
13686     /** 
13687     * @cfg {Boolean} grow 
13688     * @hide 
13689     */
13690     /** 
13691     * @cfg {Number} growMin 
13692     * @hide 
13693     */
13694     /** 
13695     * @cfg {Number} growMax 
13696     * @hide 
13697     */
13698     /**
13699      * @hide
13700      * @method autoSize
13701      */
13702 });
13703
13704 Roo.apply(Roo.bootstrap.ComboBox,  {
13705     
13706     header : {
13707         tag: 'div',
13708         cls: 'modal-header',
13709         cn: [
13710             {
13711                 tag: 'h4',
13712                 cls: 'modal-title'
13713             }
13714         ]
13715     },
13716     
13717     body : {
13718         tag: 'div',
13719         cls: 'modal-body',
13720         cn: [
13721             {
13722                 tag: 'ul',
13723                 cls: 'list-group'
13724             }
13725         ]
13726     },
13727     
13728     listItemRadio : {
13729         tag: 'li',
13730         cls: 'list-group-item',
13731         cn: [
13732             {
13733                 tag: 'span',
13734                 cls: 'roo-combobox-list-group-item-value'
13735             },
13736             {
13737                 tag: 'div',
13738                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13739                 cn: [
13740                     {
13741                         tag: 'input',
13742                         type: 'radio'
13743                     },
13744                     {
13745                         tag: 'label'
13746                     }
13747                 ]
13748             }
13749         ]
13750     },
13751     
13752     listItemCheckbox : {
13753         tag: 'li',
13754         cls: 'list-group-item',
13755         cn: [
13756             {
13757                 tag: 'span',
13758                 cls: 'roo-combobox-list-group-item-value'
13759             },
13760             {
13761                 tag: 'div',
13762                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13763                 cn: [
13764                     {
13765                         tag: 'input',
13766                         type: 'checkbox'
13767                     },
13768                     {
13769                         tag: 'label'
13770                     }
13771                 ]
13772             }
13773         ]
13774     },
13775     
13776     emptyResult : {
13777         tag: 'div',
13778         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13779     },
13780     
13781     footer : {
13782         tag: 'div',
13783         cls: 'modal-footer',
13784         cn: [
13785             {
13786                 tag: 'div',
13787                 cls: 'row',
13788                 cn: [
13789                     {
13790                         tag: 'div',
13791                         cls: 'col-xs-6 text-left',
13792                         cn: {
13793                             tag: 'button',
13794                             cls: 'btn btn-danger roo-touch-view-cancel',
13795                             html: 'Cancel'
13796                         }
13797                     },
13798                     {
13799                         tag: 'div',
13800                         cls: 'col-xs-6 text-right',
13801                         cn: {
13802                             tag: 'button',
13803                             cls: 'btn btn-success roo-touch-view-ok',
13804                             html: 'OK'
13805                         }
13806                     }
13807                 ]
13808             }
13809         ]
13810         
13811     }
13812 });
13813
13814 Roo.apply(Roo.bootstrap.ComboBox,  {
13815     
13816     touchViewTemplate : {
13817         tag: 'div',
13818         cls: 'modal fade roo-combobox-touch-view',
13819         cn: [
13820             {
13821                 tag: 'div',
13822                 cls: 'modal-dialog',
13823                 cn: [
13824                     {
13825                         tag: 'div',
13826                         cls: 'modal-content',
13827                         cn: [
13828                             Roo.bootstrap.ComboBox.header,
13829                             Roo.bootstrap.ComboBox.body,
13830                             Roo.bootstrap.ComboBox.footer
13831                         ]
13832                     }
13833                 ]
13834             }
13835         ]
13836     }
13837 });/*
13838  * Based on:
13839  * Ext JS Library 1.1.1
13840  * Copyright(c) 2006-2007, Ext JS, LLC.
13841  *
13842  * Originally Released Under LGPL - original licence link has changed is not relivant.
13843  *
13844  * Fork - LGPL
13845  * <script type="text/javascript">
13846  */
13847
13848 /**
13849  * @class Roo.View
13850  * @extends Roo.util.Observable
13851  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13852  * This class also supports single and multi selection modes. <br>
13853  * Create a data model bound view:
13854  <pre><code>
13855  var store = new Roo.data.Store(...);
13856
13857  var view = new Roo.View({
13858     el : "my-element",
13859     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13860  
13861     singleSelect: true,
13862     selectedClass: "ydataview-selected",
13863     store: store
13864  });
13865
13866  // listen for node click?
13867  view.on("click", function(vw, index, node, e){
13868  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13869  });
13870
13871  // load XML data
13872  dataModel.load("foobar.xml");
13873  </code></pre>
13874  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13875  * <br><br>
13876  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13877  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13878  * 
13879  * Note: old style constructor is still suported (container, template, config)
13880  * 
13881  * @constructor
13882  * Create a new View
13883  * @param {Object} config The config object
13884  * 
13885  */
13886 Roo.View = function(config, depreciated_tpl, depreciated_config){
13887     
13888     this.parent = false;
13889     
13890     if (typeof(depreciated_tpl) == 'undefined') {
13891         // new way.. - universal constructor.
13892         Roo.apply(this, config);
13893         this.el  = Roo.get(this.el);
13894     } else {
13895         // old format..
13896         this.el  = Roo.get(config);
13897         this.tpl = depreciated_tpl;
13898         Roo.apply(this, depreciated_config);
13899     }
13900     this.wrapEl  = this.el.wrap().wrap();
13901     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13902     
13903     
13904     if(typeof(this.tpl) == "string"){
13905         this.tpl = new Roo.Template(this.tpl);
13906     } else {
13907         // support xtype ctors..
13908         this.tpl = new Roo.factory(this.tpl, Roo);
13909     }
13910     
13911     
13912     this.tpl.compile();
13913     
13914     /** @private */
13915     this.addEvents({
13916         /**
13917          * @event beforeclick
13918          * Fires before a click is processed. Returns false to cancel the default action.
13919          * @param {Roo.View} this
13920          * @param {Number} index The index of the target node
13921          * @param {HTMLElement} node The target node
13922          * @param {Roo.EventObject} e The raw event object
13923          */
13924             "beforeclick" : true,
13925         /**
13926          * @event click
13927          * Fires when a template node is clicked.
13928          * @param {Roo.View} this
13929          * @param {Number} index The index of the target node
13930          * @param {HTMLElement} node The target node
13931          * @param {Roo.EventObject} e The raw event object
13932          */
13933             "click" : true,
13934         /**
13935          * @event dblclick
13936          * Fires when a template node is double clicked.
13937          * @param {Roo.View} this
13938          * @param {Number} index The index of the target node
13939          * @param {HTMLElement} node The target node
13940          * @param {Roo.EventObject} e The raw event object
13941          */
13942             "dblclick" : true,
13943         /**
13944          * @event contextmenu
13945          * Fires when a template node is right clicked.
13946          * @param {Roo.View} this
13947          * @param {Number} index The index of the target node
13948          * @param {HTMLElement} node The target node
13949          * @param {Roo.EventObject} e The raw event object
13950          */
13951             "contextmenu" : true,
13952         /**
13953          * @event selectionchange
13954          * Fires when the selected nodes change.
13955          * @param {Roo.View} this
13956          * @param {Array} selections Array of the selected nodes
13957          */
13958             "selectionchange" : true,
13959     
13960         /**
13961          * @event beforeselect
13962          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13963          * @param {Roo.View} this
13964          * @param {HTMLElement} node The node to be selected
13965          * @param {Array} selections Array of currently selected nodes
13966          */
13967             "beforeselect" : true,
13968         /**
13969          * @event preparedata
13970          * Fires on every row to render, to allow you to change the data.
13971          * @param {Roo.View} this
13972          * @param {Object} data to be rendered (change this)
13973          */
13974           "preparedata" : true
13975           
13976           
13977         });
13978
13979
13980
13981     this.el.on({
13982         "click": this.onClick,
13983         "dblclick": this.onDblClick,
13984         "contextmenu": this.onContextMenu,
13985         scope:this
13986     });
13987
13988     this.selections = [];
13989     this.nodes = [];
13990     this.cmp = new Roo.CompositeElementLite([]);
13991     if(this.store){
13992         this.store = Roo.factory(this.store, Roo.data);
13993         this.setStore(this.store, true);
13994     }
13995     
13996     if ( this.footer && this.footer.xtype) {
13997            
13998          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13999         
14000         this.footer.dataSource = this.store;
14001         this.footer.container = fctr;
14002         this.footer = Roo.factory(this.footer, Roo);
14003         fctr.insertFirst(this.el);
14004         
14005         // this is a bit insane - as the paging toolbar seems to detach the el..
14006 //        dom.parentNode.parentNode.parentNode
14007          // they get detached?
14008     }
14009     
14010     
14011     Roo.View.superclass.constructor.call(this);
14012     
14013     
14014 };
14015
14016 Roo.extend(Roo.View, Roo.util.Observable, {
14017     
14018      /**
14019      * @cfg {Roo.data.Store} store Data store to load data from.
14020      */
14021     store : false,
14022     
14023     /**
14024      * @cfg {String|Roo.Element} el The container element.
14025      */
14026     el : '',
14027     
14028     /**
14029      * @cfg {String|Roo.Template} tpl The template used by this View 
14030      */
14031     tpl : false,
14032     /**
14033      * @cfg {String} dataName the named area of the template to use as the data area
14034      *                          Works with domtemplates roo-name="name"
14035      */
14036     dataName: false,
14037     /**
14038      * @cfg {String} selectedClass The css class to add to selected nodes
14039      */
14040     selectedClass : "x-view-selected",
14041      /**
14042      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14043      */
14044     emptyText : "",
14045     
14046     /**
14047      * @cfg {String} text to display on mask (default Loading)
14048      */
14049     mask : false,
14050     /**
14051      * @cfg {Boolean} multiSelect Allow multiple selection
14052      */
14053     multiSelect : false,
14054     /**
14055      * @cfg {Boolean} singleSelect Allow single selection
14056      */
14057     singleSelect:  false,
14058     
14059     /**
14060      * @cfg {Boolean} toggleSelect - selecting 
14061      */
14062     toggleSelect : false,
14063     
14064     /**
14065      * @cfg {Boolean} tickable - selecting 
14066      */
14067     tickable : false,
14068     
14069     /**
14070      * Returns the element this view is bound to.
14071      * @return {Roo.Element}
14072      */
14073     getEl : function(){
14074         return this.wrapEl;
14075     },
14076     
14077     
14078
14079     /**
14080      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14081      */
14082     refresh : function(){
14083         //Roo.log('refresh');
14084         var t = this.tpl;
14085         
14086         // if we are using something like 'domtemplate', then
14087         // the what gets used is:
14088         // t.applySubtemplate(NAME, data, wrapping data..)
14089         // the outer template then get' applied with
14090         //     the store 'extra data'
14091         // and the body get's added to the
14092         //      roo-name="data" node?
14093         //      <span class='roo-tpl-{name}'></span> ?????
14094         
14095         
14096         
14097         this.clearSelections();
14098         this.el.update("");
14099         var html = [];
14100         var records = this.store.getRange();
14101         if(records.length < 1) {
14102             
14103             // is this valid??  = should it render a template??
14104             
14105             this.el.update(this.emptyText);
14106             return;
14107         }
14108         var el = this.el;
14109         if (this.dataName) {
14110             this.el.update(t.apply(this.store.meta)); //????
14111             el = this.el.child('.roo-tpl-' + this.dataName);
14112         }
14113         
14114         for(var i = 0, len = records.length; i < len; i++){
14115             var data = this.prepareData(records[i].data, i, records[i]);
14116             this.fireEvent("preparedata", this, data, i, records[i]);
14117             
14118             var d = Roo.apply({}, data);
14119             
14120             if(this.tickable){
14121                 Roo.apply(d, {'roo-id' : Roo.id()});
14122                 
14123                 var _this = this;
14124             
14125                 Roo.each(this.parent.item, function(item){
14126                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14127                         return;
14128                     }
14129                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14130                 });
14131             }
14132             
14133             html[html.length] = Roo.util.Format.trim(
14134                 this.dataName ?
14135                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14136                     t.apply(d)
14137             );
14138         }
14139         
14140         
14141         
14142         el.update(html.join(""));
14143         this.nodes = el.dom.childNodes;
14144         this.updateIndexes(0);
14145     },
14146     
14147
14148     /**
14149      * Function to override to reformat the data that is sent to
14150      * the template for each node.
14151      * DEPRICATED - use the preparedata event handler.
14152      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14153      * a JSON object for an UpdateManager bound view).
14154      */
14155     prepareData : function(data, index, record)
14156     {
14157         this.fireEvent("preparedata", this, data, index, record);
14158         return data;
14159     },
14160
14161     onUpdate : function(ds, record){
14162         // Roo.log('on update');   
14163         this.clearSelections();
14164         var index = this.store.indexOf(record);
14165         var n = this.nodes[index];
14166         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14167         n.parentNode.removeChild(n);
14168         this.updateIndexes(index, index);
14169     },
14170
14171     
14172     
14173 // --------- FIXME     
14174     onAdd : function(ds, records, index)
14175     {
14176         //Roo.log(['on Add', ds, records, index] );        
14177         this.clearSelections();
14178         if(this.nodes.length == 0){
14179             this.refresh();
14180             return;
14181         }
14182         var n = this.nodes[index];
14183         for(var i = 0, len = records.length; i < len; i++){
14184             var d = this.prepareData(records[i].data, i, records[i]);
14185             if(n){
14186                 this.tpl.insertBefore(n, d);
14187             }else{
14188                 
14189                 this.tpl.append(this.el, d);
14190             }
14191         }
14192         this.updateIndexes(index);
14193     },
14194
14195     onRemove : function(ds, record, index){
14196        // Roo.log('onRemove');
14197         this.clearSelections();
14198         var el = this.dataName  ?
14199             this.el.child('.roo-tpl-' + this.dataName) :
14200             this.el; 
14201         
14202         el.dom.removeChild(this.nodes[index]);
14203         this.updateIndexes(index);
14204     },
14205
14206     /**
14207      * Refresh an individual node.
14208      * @param {Number} index
14209      */
14210     refreshNode : function(index){
14211         this.onUpdate(this.store, this.store.getAt(index));
14212     },
14213
14214     updateIndexes : function(startIndex, endIndex){
14215         var ns = this.nodes;
14216         startIndex = startIndex || 0;
14217         endIndex = endIndex || ns.length - 1;
14218         for(var i = startIndex; i <= endIndex; i++){
14219             ns[i].nodeIndex = i;
14220         }
14221     },
14222
14223     /**
14224      * Changes the data store this view uses and refresh the view.
14225      * @param {Store} store
14226      */
14227     setStore : function(store, initial){
14228         if(!initial && this.store){
14229             this.store.un("datachanged", this.refresh);
14230             this.store.un("add", this.onAdd);
14231             this.store.un("remove", this.onRemove);
14232             this.store.un("update", this.onUpdate);
14233             this.store.un("clear", this.refresh);
14234             this.store.un("beforeload", this.onBeforeLoad);
14235             this.store.un("load", this.onLoad);
14236             this.store.un("loadexception", this.onLoad);
14237         }
14238         if(store){
14239           
14240             store.on("datachanged", this.refresh, this);
14241             store.on("add", this.onAdd, this);
14242             store.on("remove", this.onRemove, this);
14243             store.on("update", this.onUpdate, this);
14244             store.on("clear", this.refresh, this);
14245             store.on("beforeload", this.onBeforeLoad, this);
14246             store.on("load", this.onLoad, this);
14247             store.on("loadexception", this.onLoad, this);
14248         }
14249         
14250         if(store){
14251             this.refresh();
14252         }
14253     },
14254     /**
14255      * onbeforeLoad - masks the loading area.
14256      *
14257      */
14258     onBeforeLoad : function(store,opts)
14259     {
14260          //Roo.log('onBeforeLoad');   
14261         if (!opts.add) {
14262             this.el.update("");
14263         }
14264         this.el.mask(this.mask ? this.mask : "Loading" ); 
14265     },
14266     onLoad : function ()
14267     {
14268         this.el.unmask();
14269     },
14270     
14271
14272     /**
14273      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14274      * @param {HTMLElement} node
14275      * @return {HTMLElement} The template node
14276      */
14277     findItemFromChild : function(node){
14278         var el = this.dataName  ?
14279             this.el.child('.roo-tpl-' + this.dataName,true) :
14280             this.el.dom; 
14281         
14282         if(!node || node.parentNode == el){
14283                     return node;
14284             }
14285             var p = node.parentNode;
14286             while(p && p != el){
14287             if(p.parentNode == el){
14288                 return p;
14289             }
14290             p = p.parentNode;
14291         }
14292             return null;
14293     },
14294
14295     /** @ignore */
14296     onClick : function(e){
14297         var item = this.findItemFromChild(e.getTarget());
14298         if(item){
14299             var index = this.indexOf(item);
14300             if(this.onItemClick(item, index, e) !== false){
14301                 this.fireEvent("click", this, index, item, e);
14302             }
14303         }else{
14304             this.clearSelections();
14305         }
14306     },
14307
14308     /** @ignore */
14309     onContextMenu : function(e){
14310         var item = this.findItemFromChild(e.getTarget());
14311         if(item){
14312             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14313         }
14314     },
14315
14316     /** @ignore */
14317     onDblClick : function(e){
14318         var item = this.findItemFromChild(e.getTarget());
14319         if(item){
14320             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14321         }
14322     },
14323
14324     onItemClick : function(item, index, e)
14325     {
14326         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14327             return false;
14328         }
14329         if (this.toggleSelect) {
14330             var m = this.isSelected(item) ? 'unselect' : 'select';
14331             //Roo.log(m);
14332             var _t = this;
14333             _t[m](item, true, false);
14334             return true;
14335         }
14336         if(this.multiSelect || this.singleSelect){
14337             if(this.multiSelect && e.shiftKey && this.lastSelection){
14338                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14339             }else{
14340                 this.select(item, this.multiSelect && e.ctrlKey);
14341                 this.lastSelection = item;
14342             }
14343             
14344             if(!this.tickable){
14345                 e.preventDefault();
14346             }
14347             
14348         }
14349         return true;
14350     },
14351
14352     /**
14353      * Get the number of selected nodes.
14354      * @return {Number}
14355      */
14356     getSelectionCount : function(){
14357         return this.selections.length;
14358     },
14359
14360     /**
14361      * Get the currently selected nodes.
14362      * @return {Array} An array of HTMLElements
14363      */
14364     getSelectedNodes : function(){
14365         return this.selections;
14366     },
14367
14368     /**
14369      * Get the indexes of the selected nodes.
14370      * @return {Array}
14371      */
14372     getSelectedIndexes : function(){
14373         var indexes = [], s = this.selections;
14374         for(var i = 0, len = s.length; i < len; i++){
14375             indexes.push(s[i].nodeIndex);
14376         }
14377         return indexes;
14378     },
14379
14380     /**
14381      * Clear all selections
14382      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14383      */
14384     clearSelections : function(suppressEvent){
14385         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14386             this.cmp.elements = this.selections;
14387             this.cmp.removeClass(this.selectedClass);
14388             this.selections = [];
14389             if(!suppressEvent){
14390                 this.fireEvent("selectionchange", this, this.selections);
14391             }
14392         }
14393     },
14394
14395     /**
14396      * Returns true if the passed node is selected
14397      * @param {HTMLElement/Number} node The node or node index
14398      * @return {Boolean}
14399      */
14400     isSelected : function(node){
14401         var s = this.selections;
14402         if(s.length < 1){
14403             return false;
14404         }
14405         node = this.getNode(node);
14406         return s.indexOf(node) !== -1;
14407     },
14408
14409     /**
14410      * Selects nodes.
14411      * @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
14412      * @param {Boolean} keepExisting (optional) true to keep existing selections
14413      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14414      */
14415     select : function(nodeInfo, keepExisting, suppressEvent){
14416         if(nodeInfo instanceof Array){
14417             if(!keepExisting){
14418                 this.clearSelections(true);
14419             }
14420             for(var i = 0, len = nodeInfo.length; i < len; i++){
14421                 this.select(nodeInfo[i], true, true);
14422             }
14423             return;
14424         } 
14425         var node = this.getNode(nodeInfo);
14426         if(!node || this.isSelected(node)){
14427             return; // already selected.
14428         }
14429         if(!keepExisting){
14430             this.clearSelections(true);
14431         }
14432         
14433         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14434             Roo.fly(node).addClass(this.selectedClass);
14435             this.selections.push(node);
14436             if(!suppressEvent){
14437                 this.fireEvent("selectionchange", this, this.selections);
14438             }
14439         }
14440         
14441         
14442     },
14443       /**
14444      * Unselects nodes.
14445      * @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
14446      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14447      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14448      */
14449     unselect : function(nodeInfo, keepExisting, suppressEvent)
14450     {
14451         if(nodeInfo instanceof Array){
14452             Roo.each(this.selections, function(s) {
14453                 this.unselect(s, nodeInfo);
14454             }, this);
14455             return;
14456         }
14457         var node = this.getNode(nodeInfo);
14458         if(!node || !this.isSelected(node)){
14459             //Roo.log("not selected");
14460             return; // not selected.
14461         }
14462         // fireevent???
14463         var ns = [];
14464         Roo.each(this.selections, function(s) {
14465             if (s == node ) {
14466                 Roo.fly(node).removeClass(this.selectedClass);
14467
14468                 return;
14469             }
14470             ns.push(s);
14471         },this);
14472         
14473         this.selections= ns;
14474         this.fireEvent("selectionchange", this, this.selections);
14475     },
14476
14477     /**
14478      * Gets a template node.
14479      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14480      * @return {HTMLElement} The node or null if it wasn't found
14481      */
14482     getNode : function(nodeInfo){
14483         if(typeof nodeInfo == "string"){
14484             return document.getElementById(nodeInfo);
14485         }else if(typeof nodeInfo == "number"){
14486             return this.nodes[nodeInfo];
14487         }
14488         return nodeInfo;
14489     },
14490
14491     /**
14492      * Gets a range template nodes.
14493      * @param {Number} startIndex
14494      * @param {Number} endIndex
14495      * @return {Array} An array of nodes
14496      */
14497     getNodes : function(start, end){
14498         var ns = this.nodes;
14499         start = start || 0;
14500         end = typeof end == "undefined" ? ns.length - 1 : end;
14501         var nodes = [];
14502         if(start <= end){
14503             for(var i = start; i <= end; i++){
14504                 nodes.push(ns[i]);
14505             }
14506         } else{
14507             for(var i = start; i >= end; i--){
14508                 nodes.push(ns[i]);
14509             }
14510         }
14511         return nodes;
14512     },
14513
14514     /**
14515      * Finds the index of the passed node
14516      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14517      * @return {Number} The index of the node or -1
14518      */
14519     indexOf : function(node){
14520         node = this.getNode(node);
14521         if(typeof node.nodeIndex == "number"){
14522             return node.nodeIndex;
14523         }
14524         var ns = this.nodes;
14525         for(var i = 0, len = ns.length; i < len; i++){
14526             if(ns[i] == node){
14527                 return i;
14528             }
14529         }
14530         return -1;
14531     }
14532 });
14533 /*
14534  * - LGPL
14535  *
14536  * based on jquery fullcalendar
14537  * 
14538  */
14539
14540 Roo.bootstrap = Roo.bootstrap || {};
14541 /**
14542  * @class Roo.bootstrap.Calendar
14543  * @extends Roo.bootstrap.Component
14544  * Bootstrap Calendar class
14545  * @cfg {Boolean} loadMask (true|false) default false
14546  * @cfg {Object} header generate the user specific header of the calendar, default false
14547
14548  * @constructor
14549  * Create a new Container
14550  * @param {Object} config The config object
14551  */
14552
14553
14554
14555 Roo.bootstrap.Calendar = function(config){
14556     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14557      this.addEvents({
14558         /**
14559              * @event select
14560              * Fires when a date is selected
14561              * @param {DatePicker} this
14562              * @param {Date} date The selected date
14563              */
14564         'select': true,
14565         /**
14566              * @event monthchange
14567              * Fires when the displayed month changes 
14568              * @param {DatePicker} this
14569              * @param {Date} date The selected month
14570              */
14571         'monthchange': true,
14572         /**
14573              * @event evententer
14574              * Fires when mouse over an event
14575              * @param {Calendar} this
14576              * @param {event} Event
14577              */
14578         'evententer': true,
14579         /**
14580              * @event eventleave
14581              * Fires when the mouse leaves an
14582              * @param {Calendar} this
14583              * @param {event}
14584              */
14585         'eventleave': true,
14586         /**
14587              * @event eventclick
14588              * Fires when the mouse click an
14589              * @param {Calendar} this
14590              * @param {event}
14591              */
14592         'eventclick': true
14593         
14594     });
14595
14596 };
14597
14598 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14599     
14600      /**
14601      * @cfg {Number} startDay
14602      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14603      */
14604     startDay : 0,
14605     
14606     loadMask : false,
14607     
14608     header : false,
14609       
14610     getAutoCreate : function(){
14611         
14612         
14613         var fc_button = function(name, corner, style, content ) {
14614             return Roo.apply({},{
14615                 tag : 'span',
14616                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14617                          (corner.length ?
14618                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14619                             ''
14620                         ),
14621                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14622                 unselectable: 'on'
14623             });
14624         };
14625         
14626         var header = {};
14627         
14628         if(!this.header){
14629             header = {
14630                 tag : 'table',
14631                 cls : 'fc-header',
14632                 style : 'width:100%',
14633                 cn : [
14634                     {
14635                         tag: 'tr',
14636                         cn : [
14637                             {
14638                                 tag : 'td',
14639                                 cls : 'fc-header-left',
14640                                 cn : [
14641                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14642                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14643                                     { tag: 'span', cls: 'fc-header-space' },
14644                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14645
14646
14647                                 ]
14648                             },
14649
14650                             {
14651                                 tag : 'td',
14652                                 cls : 'fc-header-center',
14653                                 cn : [
14654                                     {
14655                                         tag: 'span',
14656                                         cls: 'fc-header-title',
14657                                         cn : {
14658                                             tag: 'H2',
14659                                             html : 'month / year'
14660                                         }
14661                                     }
14662
14663                                 ]
14664                             },
14665                             {
14666                                 tag : 'td',
14667                                 cls : 'fc-header-right',
14668                                 cn : [
14669                               /*      fc_button('month', 'left', '', 'month' ),
14670                                     fc_button('week', '', '', 'week' ),
14671                                     fc_button('day', 'right', '', 'day' )
14672                                 */    
14673
14674                                 ]
14675                             }
14676
14677                         ]
14678                     }
14679                 ]
14680             };
14681         }
14682         
14683         header = this.header;
14684         
14685        
14686         var cal_heads = function() {
14687             var ret = [];
14688             // fixme - handle this.
14689             
14690             for (var i =0; i < Date.dayNames.length; i++) {
14691                 var d = Date.dayNames[i];
14692                 ret.push({
14693                     tag: 'th',
14694                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14695                     html : d.substring(0,3)
14696                 });
14697                 
14698             }
14699             ret[0].cls += ' fc-first';
14700             ret[6].cls += ' fc-last';
14701             return ret;
14702         };
14703         var cal_cell = function(n) {
14704             return  {
14705                 tag: 'td',
14706                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14707                 cn : [
14708                     {
14709                         cn : [
14710                             {
14711                                 cls: 'fc-day-number',
14712                                 html: 'D'
14713                             },
14714                             {
14715                                 cls: 'fc-day-content',
14716                              
14717                                 cn : [
14718                                      {
14719                                         style: 'position: relative;' // height: 17px;
14720                                     }
14721                                 ]
14722                             }
14723                             
14724                             
14725                         ]
14726                     }
14727                 ]
14728                 
14729             }
14730         };
14731         var cal_rows = function() {
14732             
14733             var ret = [];
14734             for (var r = 0; r < 6; r++) {
14735                 var row= {
14736                     tag : 'tr',
14737                     cls : 'fc-week',
14738                     cn : []
14739                 };
14740                 
14741                 for (var i =0; i < Date.dayNames.length; i++) {
14742                     var d = Date.dayNames[i];
14743                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14744
14745                 }
14746                 row.cn[0].cls+=' fc-first';
14747                 row.cn[0].cn[0].style = 'min-height:90px';
14748                 row.cn[6].cls+=' fc-last';
14749                 ret.push(row);
14750                 
14751             }
14752             ret[0].cls += ' fc-first';
14753             ret[4].cls += ' fc-prev-last';
14754             ret[5].cls += ' fc-last';
14755             return ret;
14756             
14757         };
14758         
14759         var cal_table = {
14760             tag: 'table',
14761             cls: 'fc-border-separate',
14762             style : 'width:100%',
14763             cellspacing  : 0,
14764             cn : [
14765                 { 
14766                     tag: 'thead',
14767                     cn : [
14768                         { 
14769                             tag: 'tr',
14770                             cls : 'fc-first fc-last',
14771                             cn : cal_heads()
14772                         }
14773                     ]
14774                 },
14775                 { 
14776                     tag: 'tbody',
14777                     cn : cal_rows()
14778                 }
14779                   
14780             ]
14781         };
14782          
14783          var cfg = {
14784             cls : 'fc fc-ltr',
14785             cn : [
14786                 header,
14787                 {
14788                     cls : 'fc-content',
14789                     style : "position: relative;",
14790                     cn : [
14791                         {
14792                             cls : 'fc-view fc-view-month fc-grid',
14793                             style : 'position: relative',
14794                             unselectable : 'on',
14795                             cn : [
14796                                 {
14797                                     cls : 'fc-event-container',
14798                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14799                                 },
14800                                 cal_table
14801                             ]
14802                         }
14803                     ]
14804     
14805                 }
14806            ] 
14807             
14808         };
14809         
14810          
14811         
14812         return cfg;
14813     },
14814     
14815     
14816     initEvents : function()
14817     {
14818         if(!this.store){
14819             throw "can not find store for calendar";
14820         }
14821         
14822         var mark = {
14823             tag: "div",
14824             cls:"x-dlg-mask",
14825             style: "text-align:center",
14826             cn: [
14827                 {
14828                     tag: "div",
14829                     style: "background-color:white;width:50%;margin:250 auto",
14830                     cn: [
14831                         {
14832                             tag: "img",
14833                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14834                         },
14835                         {
14836                             tag: "span",
14837                             html: "Loading"
14838                         }
14839                         
14840                     ]
14841                 }
14842             ]
14843         };
14844         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14845         
14846         var size = this.el.select('.fc-content', true).first().getSize();
14847         this.maskEl.setSize(size.width, size.height);
14848         this.maskEl.enableDisplayMode("block");
14849         if(!this.loadMask){
14850             this.maskEl.hide();
14851         }
14852         
14853         this.store = Roo.factory(this.store, Roo.data);
14854         this.store.on('load', this.onLoad, this);
14855         this.store.on('beforeload', this.onBeforeLoad, this);
14856         
14857         this.resize();
14858         
14859         this.cells = this.el.select('.fc-day',true);
14860         //Roo.log(this.cells);
14861         this.textNodes = this.el.query('.fc-day-number');
14862         this.cells.addClassOnOver('fc-state-hover');
14863         
14864         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14865         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14866         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14867         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14868         
14869         this.on('monthchange', this.onMonthChange, this);
14870         
14871         this.update(new Date().clearTime());
14872     },
14873     
14874     resize : function() {
14875         var sz  = this.el.getSize();
14876         
14877         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14878         this.el.select('.fc-day-content div',true).setHeight(34);
14879     },
14880     
14881     
14882     // private
14883     showPrevMonth : function(e){
14884         this.update(this.activeDate.add("mo", -1));
14885     },
14886     showToday : function(e){
14887         this.update(new Date().clearTime());
14888     },
14889     // private
14890     showNextMonth : function(e){
14891         this.update(this.activeDate.add("mo", 1));
14892     },
14893
14894     // private
14895     showPrevYear : function(){
14896         this.update(this.activeDate.add("y", -1));
14897     },
14898
14899     // private
14900     showNextYear : function(){
14901         this.update(this.activeDate.add("y", 1));
14902     },
14903
14904     
14905    // private
14906     update : function(date)
14907     {
14908         var vd = this.activeDate;
14909         this.activeDate = date;
14910 //        if(vd && this.el){
14911 //            var t = date.getTime();
14912 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14913 //                Roo.log('using add remove');
14914 //                
14915 //                this.fireEvent('monthchange', this, date);
14916 //                
14917 //                this.cells.removeClass("fc-state-highlight");
14918 //                this.cells.each(function(c){
14919 //                   if(c.dateValue == t){
14920 //                       c.addClass("fc-state-highlight");
14921 //                       setTimeout(function(){
14922 //                            try{c.dom.firstChild.focus();}catch(e){}
14923 //                       }, 50);
14924 //                       return false;
14925 //                   }
14926 //                   return true;
14927 //                });
14928 //                return;
14929 //            }
14930 //        }
14931         
14932         var days = date.getDaysInMonth();
14933         
14934         var firstOfMonth = date.getFirstDateOfMonth();
14935         var startingPos = firstOfMonth.getDay()-this.startDay;
14936         
14937         if(startingPos < this.startDay){
14938             startingPos += 7;
14939         }
14940         
14941         var pm = date.add(Date.MONTH, -1);
14942         var prevStart = pm.getDaysInMonth()-startingPos;
14943 //        
14944         this.cells = this.el.select('.fc-day',true);
14945         this.textNodes = this.el.query('.fc-day-number');
14946         this.cells.addClassOnOver('fc-state-hover');
14947         
14948         var cells = this.cells.elements;
14949         var textEls = this.textNodes;
14950         
14951         Roo.each(cells, function(cell){
14952             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14953         });
14954         
14955         days += startingPos;
14956
14957         // convert everything to numbers so it's fast
14958         var day = 86400000;
14959         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14960         //Roo.log(d);
14961         //Roo.log(pm);
14962         //Roo.log(prevStart);
14963         
14964         var today = new Date().clearTime().getTime();
14965         var sel = date.clearTime().getTime();
14966         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14967         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14968         var ddMatch = this.disabledDatesRE;
14969         var ddText = this.disabledDatesText;
14970         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14971         var ddaysText = this.disabledDaysText;
14972         var format = this.format;
14973         
14974         var setCellClass = function(cal, cell){
14975             cell.row = 0;
14976             cell.events = [];
14977             cell.more = [];
14978             //Roo.log('set Cell Class');
14979             cell.title = "";
14980             var t = d.getTime();
14981             
14982             //Roo.log(d);
14983             
14984             cell.dateValue = t;
14985             if(t == today){
14986                 cell.className += " fc-today";
14987                 cell.className += " fc-state-highlight";
14988                 cell.title = cal.todayText;
14989             }
14990             if(t == sel){
14991                 // disable highlight in other month..
14992                 //cell.className += " fc-state-highlight";
14993                 
14994             }
14995             // disabling
14996             if(t < min) {
14997                 cell.className = " fc-state-disabled";
14998                 cell.title = cal.minText;
14999                 return;
15000             }
15001             if(t > max) {
15002                 cell.className = " fc-state-disabled";
15003                 cell.title = cal.maxText;
15004                 return;
15005             }
15006             if(ddays){
15007                 if(ddays.indexOf(d.getDay()) != -1){
15008                     cell.title = ddaysText;
15009                     cell.className = " fc-state-disabled";
15010                 }
15011             }
15012             if(ddMatch && format){
15013                 var fvalue = d.dateFormat(format);
15014                 if(ddMatch.test(fvalue)){
15015                     cell.title = ddText.replace("%0", fvalue);
15016                     cell.className = " fc-state-disabled";
15017                 }
15018             }
15019             
15020             if (!cell.initialClassName) {
15021                 cell.initialClassName = cell.dom.className;
15022             }
15023             
15024             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15025         };
15026
15027         var i = 0;
15028         
15029         for(; i < startingPos; i++) {
15030             textEls[i].innerHTML = (++prevStart);
15031             d.setDate(d.getDate()+1);
15032             
15033             cells[i].className = "fc-past fc-other-month";
15034             setCellClass(this, cells[i]);
15035         }
15036         
15037         var intDay = 0;
15038         
15039         for(; i < days; i++){
15040             intDay = i - startingPos + 1;
15041             textEls[i].innerHTML = (intDay);
15042             d.setDate(d.getDate()+1);
15043             
15044             cells[i].className = ''; // "x-date-active";
15045             setCellClass(this, cells[i]);
15046         }
15047         var extraDays = 0;
15048         
15049         for(; i < 42; i++) {
15050             textEls[i].innerHTML = (++extraDays);
15051             d.setDate(d.getDate()+1);
15052             
15053             cells[i].className = "fc-future fc-other-month";
15054             setCellClass(this, cells[i]);
15055         }
15056         
15057         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15058         
15059         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15060         
15061         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15062         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15063         
15064         if(totalRows != 6){
15065             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15066             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15067         }
15068         
15069         this.fireEvent('monthchange', this, date);
15070         
15071         
15072         /*
15073         if(!this.internalRender){
15074             var main = this.el.dom.firstChild;
15075             var w = main.offsetWidth;
15076             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15077             Roo.fly(main).setWidth(w);
15078             this.internalRender = true;
15079             // opera does not respect the auto grow header center column
15080             // then, after it gets a width opera refuses to recalculate
15081             // without a second pass
15082             if(Roo.isOpera && !this.secondPass){
15083                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15084                 this.secondPass = true;
15085                 this.update.defer(10, this, [date]);
15086             }
15087         }
15088         */
15089         
15090     },
15091     
15092     findCell : function(dt) {
15093         dt = dt.clearTime().getTime();
15094         var ret = false;
15095         this.cells.each(function(c){
15096             //Roo.log("check " +c.dateValue + '?=' + dt);
15097             if(c.dateValue == dt){
15098                 ret = c;
15099                 return false;
15100             }
15101             return true;
15102         });
15103         
15104         return ret;
15105     },
15106     
15107     findCells : function(ev) {
15108         var s = ev.start.clone().clearTime().getTime();
15109        // Roo.log(s);
15110         var e= ev.end.clone().clearTime().getTime();
15111        // Roo.log(e);
15112         var ret = [];
15113         this.cells.each(function(c){
15114              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15115             
15116             if(c.dateValue > e){
15117                 return ;
15118             }
15119             if(c.dateValue < s){
15120                 return ;
15121             }
15122             ret.push(c);
15123         });
15124         
15125         return ret;    
15126     },
15127     
15128 //    findBestRow: function(cells)
15129 //    {
15130 //        var ret = 0;
15131 //        
15132 //        for (var i =0 ; i < cells.length;i++) {
15133 //            ret  = Math.max(cells[i].rows || 0,ret);
15134 //        }
15135 //        return ret;
15136 //        
15137 //    },
15138     
15139     
15140     addItem : function(ev)
15141     {
15142         // look for vertical location slot in
15143         var cells = this.findCells(ev);
15144         
15145 //        ev.row = this.findBestRow(cells);
15146         
15147         // work out the location.
15148         
15149         var crow = false;
15150         var rows = [];
15151         for(var i =0; i < cells.length; i++) {
15152             
15153             cells[i].row = cells[0].row;
15154             
15155             if(i == 0){
15156                 cells[i].row = cells[i].row + 1;
15157             }
15158             
15159             if (!crow) {
15160                 crow = {
15161                     start : cells[i],
15162                     end :  cells[i]
15163                 };
15164                 continue;
15165             }
15166             if (crow.start.getY() == cells[i].getY()) {
15167                 // on same row.
15168                 crow.end = cells[i];
15169                 continue;
15170             }
15171             // different row.
15172             rows.push(crow);
15173             crow = {
15174                 start: cells[i],
15175                 end : cells[i]
15176             };
15177             
15178         }
15179         
15180         rows.push(crow);
15181         ev.els = [];
15182         ev.rows = rows;
15183         ev.cells = cells;
15184         
15185         cells[0].events.push(ev);
15186         
15187         this.calevents.push(ev);
15188     },
15189     
15190     clearEvents: function() {
15191         
15192         if(!this.calevents){
15193             return;
15194         }
15195         
15196         Roo.each(this.cells.elements, function(c){
15197             c.row = 0;
15198             c.events = [];
15199             c.more = [];
15200         });
15201         
15202         Roo.each(this.calevents, function(e) {
15203             Roo.each(e.els, function(el) {
15204                 el.un('mouseenter' ,this.onEventEnter, this);
15205                 el.un('mouseleave' ,this.onEventLeave, this);
15206                 el.remove();
15207             },this);
15208         },this);
15209         
15210         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15211             e.remove();
15212         });
15213         
15214     },
15215     
15216     renderEvents: function()
15217     {   
15218         var _this = this;
15219         
15220         this.cells.each(function(c) {
15221             
15222             if(c.row < 5){
15223                 return;
15224             }
15225             
15226             var ev = c.events;
15227             
15228             var r = 4;
15229             if(c.row != c.events.length){
15230                 r = 4 - (4 - (c.row - c.events.length));
15231             }
15232             
15233             c.events = ev.slice(0, r);
15234             c.more = ev.slice(r);
15235             
15236             if(c.more.length && c.more.length == 1){
15237                 c.events.push(c.more.pop());
15238             }
15239             
15240             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15241             
15242         });
15243             
15244         this.cells.each(function(c) {
15245             
15246             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15247             
15248             
15249             for (var e = 0; e < c.events.length; e++){
15250                 var ev = c.events[e];
15251                 var rows = ev.rows;
15252                 
15253                 for(var i = 0; i < rows.length; i++) {
15254                 
15255                     // how many rows should it span..
15256
15257                     var  cfg = {
15258                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15259                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15260
15261                         unselectable : "on",
15262                         cn : [
15263                             {
15264                                 cls: 'fc-event-inner',
15265                                 cn : [
15266     //                                {
15267     //                                  tag:'span',
15268     //                                  cls: 'fc-event-time',
15269     //                                  html : cells.length > 1 ? '' : ev.time
15270     //                                },
15271                                     {
15272                                       tag:'span',
15273                                       cls: 'fc-event-title',
15274                                       html : String.format('{0}', ev.title)
15275                                     }
15276
15277
15278                                 ]
15279                             },
15280                             {
15281                                 cls: 'ui-resizable-handle ui-resizable-e',
15282                                 html : '&nbsp;&nbsp;&nbsp'
15283                             }
15284
15285                         ]
15286                     };
15287
15288                     if (i == 0) {
15289                         cfg.cls += ' fc-event-start';
15290                     }
15291                     if ((i+1) == rows.length) {
15292                         cfg.cls += ' fc-event-end';
15293                     }
15294
15295                     var ctr = _this.el.select('.fc-event-container',true).first();
15296                     var cg = ctr.createChild(cfg);
15297
15298                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15299                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15300
15301                     var r = (c.more.length) ? 1 : 0;
15302                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15303                     cg.setWidth(ebox.right - sbox.x -2);
15304
15305                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15306                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15307                     cg.on('click', _this.onEventClick, _this, ev);
15308
15309                     ev.els.push(cg);
15310                     
15311                 }
15312                 
15313             }
15314             
15315             
15316             if(c.more.length){
15317                 var  cfg = {
15318                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15319                     style : 'position: absolute',
15320                     unselectable : "on",
15321                     cn : [
15322                         {
15323                             cls: 'fc-event-inner',
15324                             cn : [
15325                                 {
15326                                   tag:'span',
15327                                   cls: 'fc-event-title',
15328                                   html : 'More'
15329                                 }
15330
15331
15332                             ]
15333                         },
15334                         {
15335                             cls: 'ui-resizable-handle ui-resizable-e',
15336                             html : '&nbsp;&nbsp;&nbsp'
15337                         }
15338
15339                     ]
15340                 };
15341
15342                 var ctr = _this.el.select('.fc-event-container',true).first();
15343                 var cg = ctr.createChild(cfg);
15344
15345                 var sbox = c.select('.fc-day-content',true).first().getBox();
15346                 var ebox = c.select('.fc-day-content',true).first().getBox();
15347                 //Roo.log(cg);
15348                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15349                 cg.setWidth(ebox.right - sbox.x -2);
15350
15351                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15352                 
15353             }
15354             
15355         });
15356         
15357         
15358         
15359     },
15360     
15361     onEventEnter: function (e, el,event,d) {
15362         this.fireEvent('evententer', this, el, event);
15363     },
15364     
15365     onEventLeave: function (e, el,event,d) {
15366         this.fireEvent('eventleave', this, el, event);
15367     },
15368     
15369     onEventClick: function (e, el,event,d) {
15370         this.fireEvent('eventclick', this, el, event);
15371     },
15372     
15373     onMonthChange: function () {
15374         this.store.load();
15375     },
15376     
15377     onMoreEventClick: function(e, el, more)
15378     {
15379         var _this = this;
15380         
15381         this.calpopover.placement = 'right';
15382         this.calpopover.setTitle('More');
15383         
15384         this.calpopover.setContent('');
15385         
15386         var ctr = this.calpopover.el.select('.popover-content', true).first();
15387         
15388         Roo.each(more, function(m){
15389             var cfg = {
15390                 cls : 'fc-event-hori fc-event-draggable',
15391                 html : m.title
15392             };
15393             var cg = ctr.createChild(cfg);
15394             
15395             cg.on('click', _this.onEventClick, _this, m);
15396         });
15397         
15398         this.calpopover.show(el);
15399         
15400         
15401     },
15402     
15403     onLoad: function () 
15404     {   
15405         this.calevents = [];
15406         var cal = this;
15407         
15408         if(this.store.getCount() > 0){
15409             this.store.data.each(function(d){
15410                cal.addItem({
15411                     id : d.data.id,
15412                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15413                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15414                     time : d.data.start_time,
15415                     title : d.data.title,
15416                     description : d.data.description,
15417                     venue : d.data.venue
15418                 });
15419             });
15420         }
15421         
15422         this.renderEvents();
15423         
15424         if(this.calevents.length && this.loadMask){
15425             this.maskEl.hide();
15426         }
15427     },
15428     
15429     onBeforeLoad: function()
15430     {
15431         this.clearEvents();
15432         if(this.loadMask){
15433             this.maskEl.show();
15434         }
15435     }
15436 });
15437
15438  
15439  /*
15440  * - LGPL
15441  *
15442  * element
15443  * 
15444  */
15445
15446 /**
15447  * @class Roo.bootstrap.Popover
15448  * @extends Roo.bootstrap.Component
15449  * Bootstrap Popover class
15450  * @cfg {String} html contents of the popover   (or false to use children..)
15451  * @cfg {String} title of popover (or false to hide)
15452  * @cfg {String} placement how it is placed
15453  * @cfg {String} trigger click || hover (or false to trigger manually)
15454  * @cfg {String} over what (parent or false to trigger manually.)
15455  * @cfg {Number} delay - delay before showing
15456  
15457  * @constructor
15458  * Create a new Popover
15459  * @param {Object} config The config object
15460  */
15461
15462 Roo.bootstrap.Popover = function(config){
15463     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15464 };
15465
15466 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15467     
15468     title: 'Fill in a title',
15469     html: false,
15470     
15471     placement : 'right',
15472     trigger : 'hover', // hover
15473     
15474     delay : 0,
15475     
15476     over: 'parent',
15477     
15478     can_build_overlaid : false,
15479     
15480     getChildContainer : function()
15481     {
15482         return this.el.select('.popover-content',true).first();
15483     },
15484     
15485     getAutoCreate : function(){
15486          Roo.log('make popover?');
15487         var cfg = {
15488            cls : 'popover roo-dynamic',
15489            style: 'display:block',
15490            cn : [
15491                 {
15492                     cls : 'arrow'
15493                 },
15494                 {
15495                     cls : 'popover-inner',
15496                     cn : [
15497                         {
15498                             tag: 'h3',
15499                             cls: 'popover-title',
15500                             html : this.title
15501                         },
15502                         {
15503                             cls : 'popover-content',
15504                             html : this.html
15505                         }
15506                     ]
15507                     
15508                 }
15509            ]
15510         };
15511         
15512         return cfg;
15513     },
15514     setTitle: function(str)
15515     {
15516         this.title = str;
15517         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15518     },
15519     setContent: function(str)
15520     {
15521         this.html = str;
15522         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15523     },
15524     // as it get's added to the bottom of the page.
15525     onRender : function(ct, position)
15526     {
15527         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15528         if(!this.el){
15529             var cfg = Roo.apply({},  this.getAutoCreate());
15530             cfg.id = Roo.id();
15531             
15532             if (this.cls) {
15533                 cfg.cls += ' ' + this.cls;
15534             }
15535             if (this.style) {
15536                 cfg.style = this.style;
15537             }
15538             Roo.log("adding to ")
15539             this.el = Roo.get(document.body).createChild(cfg, position);
15540             Roo.log(this.el);
15541         }
15542         this.initEvents();
15543     },
15544     
15545     initEvents : function()
15546     {
15547         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15548         this.el.enableDisplayMode('block');
15549         this.el.hide();
15550         if (this.over === false) {
15551             return; 
15552         }
15553         if (this.triggers === false) {
15554             return;
15555         }
15556         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15557         var triggers = this.trigger ? this.trigger.split(' ') : [];
15558         Roo.each(triggers, function(trigger) {
15559         
15560             if (trigger == 'click') {
15561                 on_el.on('click', this.toggle, this);
15562             } else if (trigger != 'manual') {
15563                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15564                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15565       
15566                 on_el.on(eventIn  ,this.enter, this);
15567                 on_el.on(eventOut, this.leave, this);
15568             }
15569         }, this);
15570         
15571     },
15572     
15573     
15574     // private
15575     timeout : null,
15576     hoverState : null,
15577     
15578     toggle : function () {
15579         this.hoverState == 'in' ? this.leave() : this.enter();
15580     },
15581     
15582     enter : function () {
15583        
15584     
15585         clearTimeout(this.timeout);
15586     
15587         this.hoverState = 'in';
15588     
15589         if (!this.delay || !this.delay.show) {
15590             this.show();
15591             return;
15592         }
15593         var _t = this;
15594         this.timeout = setTimeout(function () {
15595             if (_t.hoverState == 'in') {
15596                 _t.show();
15597             }
15598         }, this.delay.show)
15599     },
15600     leave : function() {
15601         clearTimeout(this.timeout);
15602     
15603         this.hoverState = 'out';
15604     
15605         if (!this.delay || !this.delay.hide) {
15606             this.hide();
15607             return;
15608         }
15609         var _t = this;
15610         this.timeout = setTimeout(function () {
15611             if (_t.hoverState == 'out') {
15612                 _t.hide();
15613             }
15614         }, this.delay.hide)
15615     },
15616     
15617     show : function (on_el)
15618     {
15619         if (!on_el) {
15620             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15621         }
15622         // set content.
15623         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15624         if (this.html !== false) {
15625             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15626         }
15627         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15628         if (!this.title.length) {
15629             this.el.select('.popover-title',true).hide();
15630         }
15631         
15632         var placement = typeof this.placement == 'function' ?
15633             this.placement.call(this, this.el, on_el) :
15634             this.placement;
15635             
15636         var autoToken = /\s?auto?\s?/i;
15637         var autoPlace = autoToken.test(placement);
15638         if (autoPlace) {
15639             placement = placement.replace(autoToken, '') || 'top';
15640         }
15641         
15642         //this.el.detach()
15643         //this.el.setXY([0,0]);
15644         this.el.show();
15645         this.el.dom.style.display='block';
15646         this.el.addClass(placement);
15647         
15648         //this.el.appendTo(on_el);
15649         
15650         var p = this.getPosition();
15651         var box = this.el.getBox();
15652         
15653         if (autoPlace) {
15654             // fixme..
15655         }
15656         var align = Roo.bootstrap.Popover.alignment[placement];
15657         this.el.alignTo(on_el, align[0],align[1]);
15658         //var arrow = this.el.select('.arrow',true).first();
15659         //arrow.set(align[2], 
15660         
15661         this.el.addClass('in');
15662         
15663         
15664         if (this.el.hasClass('fade')) {
15665             // fade it?
15666         }
15667         
15668     },
15669     hide : function()
15670     {
15671         this.el.setXY([0,0]);
15672         this.el.removeClass('in');
15673         this.el.hide();
15674         this.hoverState = null;
15675         
15676     }
15677     
15678 });
15679
15680 Roo.bootstrap.Popover.alignment = {
15681     'left' : ['r-l', [-10,0], 'right'],
15682     'right' : ['l-r', [10,0], 'left'],
15683     'bottom' : ['t-b', [0,10], 'top'],
15684     'top' : [ 'b-t', [0,-10], 'bottom']
15685 };
15686
15687  /*
15688  * - LGPL
15689  *
15690  * Progress
15691  * 
15692  */
15693
15694 /**
15695  * @class Roo.bootstrap.Progress
15696  * @extends Roo.bootstrap.Component
15697  * Bootstrap Progress class
15698  * @cfg {Boolean} striped striped of the progress bar
15699  * @cfg {Boolean} active animated of the progress bar
15700  * 
15701  * 
15702  * @constructor
15703  * Create a new Progress
15704  * @param {Object} config The config object
15705  */
15706
15707 Roo.bootstrap.Progress = function(config){
15708     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15709 };
15710
15711 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15712     
15713     striped : false,
15714     active: false,
15715     
15716     getAutoCreate : function(){
15717         var cfg = {
15718             tag: 'div',
15719             cls: 'progress'
15720         };
15721         
15722         
15723         if(this.striped){
15724             cfg.cls += ' progress-striped';
15725         }
15726       
15727         if(this.active){
15728             cfg.cls += ' active';
15729         }
15730         
15731         
15732         return cfg;
15733     }
15734    
15735 });
15736
15737  
15738
15739  /*
15740  * - LGPL
15741  *
15742  * ProgressBar
15743  * 
15744  */
15745
15746 /**
15747  * @class Roo.bootstrap.ProgressBar
15748  * @extends Roo.bootstrap.Component
15749  * Bootstrap ProgressBar class
15750  * @cfg {Number} aria_valuenow aria-value now
15751  * @cfg {Number} aria_valuemin aria-value min
15752  * @cfg {Number} aria_valuemax aria-value max
15753  * @cfg {String} label label for the progress bar
15754  * @cfg {String} panel (success | info | warning | danger )
15755  * @cfg {String} role role of the progress bar
15756  * @cfg {String} sr_only text
15757  * 
15758  * 
15759  * @constructor
15760  * Create a new ProgressBar
15761  * @param {Object} config The config object
15762  */
15763
15764 Roo.bootstrap.ProgressBar = function(config){
15765     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15766 };
15767
15768 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15769     
15770     aria_valuenow : 0,
15771     aria_valuemin : 0,
15772     aria_valuemax : 100,
15773     label : false,
15774     panel : false,
15775     role : false,
15776     sr_only: false,
15777     
15778     getAutoCreate : function()
15779     {
15780         
15781         var cfg = {
15782             tag: 'div',
15783             cls: 'progress-bar',
15784             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15785         };
15786         
15787         if(this.sr_only){
15788             cfg.cn = {
15789                 tag: 'span',
15790                 cls: 'sr-only',
15791                 html: this.sr_only
15792             }
15793         }
15794         
15795         if(this.role){
15796             cfg.role = this.role;
15797         }
15798         
15799         if(this.aria_valuenow){
15800             cfg['aria-valuenow'] = this.aria_valuenow;
15801         }
15802         
15803         if(this.aria_valuemin){
15804             cfg['aria-valuemin'] = this.aria_valuemin;
15805         }
15806         
15807         if(this.aria_valuemax){
15808             cfg['aria-valuemax'] = this.aria_valuemax;
15809         }
15810         
15811         if(this.label && !this.sr_only){
15812             cfg.html = this.label;
15813         }
15814         
15815         if(this.panel){
15816             cfg.cls += ' progress-bar-' + this.panel;
15817         }
15818         
15819         return cfg;
15820     },
15821     
15822     update : function(aria_valuenow)
15823     {
15824         this.aria_valuenow = aria_valuenow;
15825         
15826         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15827     }
15828    
15829 });
15830
15831  
15832
15833  /*
15834  * - LGPL
15835  *
15836  * column
15837  * 
15838  */
15839
15840 /**
15841  * @class Roo.bootstrap.TabGroup
15842  * @extends Roo.bootstrap.Column
15843  * Bootstrap Column class
15844  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15845  * @cfg {Boolean} carousel true to make the group behave like a carousel
15846  * @cfg {Boolean} bullets show bullets for the panels
15847  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15848  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15849  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15850  * 
15851  * @constructor
15852  * Create a new TabGroup
15853  * @param {Object} config The config object
15854  */
15855
15856 Roo.bootstrap.TabGroup = function(config){
15857     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15858     if (!this.navId) {
15859         this.navId = Roo.id();
15860     }
15861     this.tabs = [];
15862     Roo.bootstrap.TabGroup.register(this);
15863     
15864 };
15865
15866 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15867     
15868     carousel : false,
15869     transition : false,
15870     bullets : 0,
15871     timer : 0,
15872     autoslide : false,
15873     slideFn : false,
15874     slideOnTouch : false,
15875     
15876     getAutoCreate : function()
15877     {
15878         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15879         
15880         cfg.cls += ' tab-content';
15881         
15882         Roo.log('get auto create...............');
15883         
15884         if (this.carousel) {
15885             cfg.cls += ' carousel slide';
15886             
15887             cfg.cn = [{
15888                cls : 'carousel-inner'
15889             }];
15890         
15891             if(this.bullets  && !Roo.isTouch){
15892                 
15893                 var bullets = {
15894                     cls : 'carousel-bullets',
15895                     cn : []
15896                 };
15897                
15898                 if(this.bullets_cls){
15899                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15900                 }
15901                  /*
15902                 for (var i = 0; i < this.bullets; i++){
15903                     bullets.cn.push({
15904                         cls : 'bullet bullet-' + i
15905                     });
15906                 }
15907                 */
15908                 bullets.cn.push({
15909                     cls : 'clear'
15910                 });
15911                 
15912                 cfg.cn[0].cn = bullets;
15913             }
15914         }
15915         
15916         return cfg;
15917     },
15918     
15919     initEvents:  function()
15920     {
15921         Roo.log('-------- init events on tab group ---------');
15922         
15923         
15924         
15925         Roo.log(this);
15926         
15927         if(Roo.isTouch && this.slideOnTouch){
15928             this.el.on("touchstart", this.onTouchStart, this);
15929         }
15930         
15931         if(this.autoslide){
15932             var _this = this;
15933             
15934             this.slideFn = window.setInterval(function() {
15935                 _this.showPanelNext();
15936             }, this.timer);
15937         }
15938         
15939     },
15940     
15941     onTouchStart : function(e, el, o)
15942     {
15943         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15944             return;
15945         }
15946         
15947         this.showPanelNext();
15948     },
15949     
15950     getChildContainer : function()
15951     {
15952         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15953     },
15954     
15955     /**
15956     * register a Navigation item
15957     * @param {Roo.bootstrap.NavItem} the navitem to add
15958     */
15959     register : function(item)
15960     {
15961         this.tabs.push( item);
15962         item.navId = this.navId; // not really needed..
15963         this.addBullet();
15964     
15965     },
15966     
15967     getActivePanel : function()
15968     {
15969         var r = false;
15970         Roo.each(this.tabs, function(t) {
15971             if (t.active) {
15972                 r = t;
15973                 return false;
15974             }
15975             return null;
15976         });
15977         return r;
15978         
15979     },
15980     getPanelByName : function(n)
15981     {
15982         var r = false;
15983         Roo.each(this.tabs, function(t) {
15984             if (t.tabId == n) {
15985                 r = t;
15986                 return false;
15987             }
15988             return null;
15989         });
15990         return r;
15991     },
15992     indexOfPanel : function(p)
15993     {
15994         var r = false;
15995         Roo.each(this.tabs, function(t,i) {
15996             if (t.tabId == p.tabId) {
15997                 r = i;
15998                 return false;
15999             }
16000             return null;
16001         });
16002         return r;
16003     },
16004     /**
16005      * show a specific panel
16006      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16007      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16008      */
16009     showPanel : function (pan)
16010     {
16011         if(this.transition){
16012             Roo.log("waiting for the transitionend");
16013             return;
16014         }
16015         
16016         if (typeof(pan) == 'number') {
16017             pan = this.tabs[pan];
16018         }
16019         if (typeof(pan) == 'string') {
16020             pan = this.getPanelByName(pan);
16021         }
16022         if (pan.tabId == this.getActivePanel().tabId) {
16023             return true;
16024         }
16025         var cur = this.getActivePanel();
16026         
16027         if (false === cur.fireEvent('beforedeactivate')) {
16028             return false;
16029         }
16030         
16031         if(this.bullets > 0 && !Roo.isTouch){
16032             this.setActiveBullet(this.indexOfPanel(pan));
16033         }
16034         
16035         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16036             
16037             this.transition = true;
16038             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16039             var lr = dir == 'next' ? 'left' : 'right';
16040             pan.el.addClass(dir); // or prev
16041             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16042             cur.el.addClass(lr); // or right
16043             pan.el.addClass(lr);
16044             
16045             var _this = this;
16046             cur.el.on('transitionend', function() {
16047                 Roo.log("trans end?");
16048                 
16049                 pan.el.removeClass([lr,dir]);
16050                 pan.setActive(true);
16051                 
16052                 cur.el.removeClass([lr]);
16053                 cur.setActive(false);
16054                 
16055                 _this.transition = false;
16056                 
16057             }, this, { single:  true } );
16058             
16059             return true;
16060         }
16061         
16062         cur.setActive(false);
16063         pan.setActive(true);
16064         
16065         return true;
16066         
16067     },
16068     showPanelNext : function()
16069     {
16070         var i = this.indexOfPanel(this.getActivePanel());
16071         
16072         if (i >= this.tabs.length - 1 && !this.autoslide) {
16073             return;
16074         }
16075         
16076         if (i >= this.tabs.length - 1 && this.autoslide) {
16077             i = -1;
16078         }
16079         
16080         this.showPanel(this.tabs[i+1]);
16081     },
16082     
16083     showPanelPrev : function()
16084     {
16085         var i = this.indexOfPanel(this.getActivePanel());
16086         
16087         if (i  < 1 && !this.autoslide) {
16088             return;
16089         }
16090         
16091         if (i < 1 && this.autoslide) {
16092             i = this.tabs.length;
16093         }
16094         
16095         this.showPanel(this.tabs[i-1]);
16096     },
16097     
16098     
16099     addBullet: function()
16100     {
16101         if(!this.bullets || Roo.isTouch){
16102             return;
16103         }
16104         var ctr = this.el.select('.carousel-bullets',true).first();
16105         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16106         var bullet = ctr.createChild({
16107             cls : 'bullet bullet-' + i
16108         },ctr.dom.lastChild);
16109         
16110         bullet.on('click', (function(e, el, o, ii, t){
16111
16112             e.preventDefault();
16113
16114             this.showPanel(ii);
16115
16116             if(this.autoslide && this.slideFn){
16117                 clearInterval(this.slideFn);
16118                 this.slideFn = window.setInterval(function() {
16119                     this.showPanelNext();
16120                 }, this.timer);
16121             }
16122
16123         }).createDelegate(this, [i, bullet], true));
16124                 
16125         
16126     },
16127      
16128     setActiveBullet : function(i)
16129     {
16130         if(Roo.isTouch){
16131             return;
16132         }
16133         
16134         Roo.each(this.el.select('.bullet', true).elements, function(el){
16135             el.removeClass('selected');
16136         });
16137
16138         var bullet = this.el.select('.bullet-' + i, true).first();
16139         
16140         if(!bullet){
16141             return;
16142         }
16143         
16144         bullet.addClass('selected');
16145     }
16146     
16147     
16148   
16149 });
16150
16151  
16152
16153  
16154  
16155 Roo.apply(Roo.bootstrap.TabGroup, {
16156     
16157     groups: {},
16158      /**
16159     * register a Navigation Group
16160     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16161     */
16162     register : function(navgrp)
16163     {
16164         this.groups[navgrp.navId] = navgrp;
16165         
16166     },
16167     /**
16168     * fetch a Navigation Group based on the navigation ID
16169     * if one does not exist , it will get created.
16170     * @param {string} the navgroup to add
16171     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16172     */
16173     get: function(navId) {
16174         if (typeof(this.groups[navId]) == 'undefined') {
16175             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16176         }
16177         return this.groups[navId] ;
16178     }
16179     
16180     
16181     
16182 });
16183
16184  /*
16185  * - LGPL
16186  *
16187  * TabPanel
16188  * 
16189  */
16190
16191 /**
16192  * @class Roo.bootstrap.TabPanel
16193  * @extends Roo.bootstrap.Component
16194  * Bootstrap TabPanel class
16195  * @cfg {Boolean} active panel active
16196  * @cfg {String} html panel content
16197  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16198  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16199  * 
16200  * 
16201  * @constructor
16202  * Create a new TabPanel
16203  * @param {Object} config The config object
16204  */
16205
16206 Roo.bootstrap.TabPanel = function(config){
16207     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16208     this.addEvents({
16209         /**
16210              * @event changed
16211              * Fires when the active status changes
16212              * @param {Roo.bootstrap.TabPanel} this
16213              * @param {Boolean} state the new state
16214             
16215          */
16216         'changed': true,
16217         /**
16218              * @event beforedeactivate
16219              * Fires before a tab is de-activated - can be used to do validation on a form.
16220              * @param {Roo.bootstrap.TabPanel} this
16221              * @return {Boolean} false if there is an error
16222             
16223          */
16224         'beforedeactivate': true
16225      });
16226     
16227     this.tabId = this.tabId || Roo.id();
16228   
16229 };
16230
16231 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16232     
16233     active: false,
16234     html: false,
16235     tabId: false,
16236     navId : false,
16237     
16238     getAutoCreate : function(){
16239         var cfg = {
16240             tag: 'div',
16241             // item is needed for carousel - not sure if it has any effect otherwise
16242             cls: 'tab-pane item',
16243             html: this.html || ''
16244         };
16245         
16246         if(this.active){
16247             cfg.cls += ' active';
16248         }
16249         
16250         if(this.tabId){
16251             cfg.tabId = this.tabId;
16252         }
16253         
16254         
16255         return cfg;
16256     },
16257     
16258     initEvents:  function()
16259     {
16260         Roo.log('-------- init events on tab panel ---------');
16261         
16262         var p = this.parent();
16263         this.navId = this.navId || p.navId;
16264         
16265         if (typeof(this.navId) != 'undefined') {
16266             // not really needed.. but just in case.. parent should be a NavGroup.
16267             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16268             Roo.log(['register', tg, this]);
16269             tg.register(this);
16270             
16271             var i = tg.tabs.length - 1;
16272             
16273             if(this.active && tg.bullets > 0 && i < tg.bullets){
16274                 tg.setActiveBullet(i);
16275             }
16276         }
16277         
16278     },
16279     
16280     
16281     onRender : function(ct, position)
16282     {
16283        // Roo.log("Call onRender: " + this.xtype);
16284         
16285         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16286         
16287         
16288         
16289         
16290         
16291     },
16292     
16293     setActive: function(state)
16294     {
16295         Roo.log("panel - set active " + this.tabId + "=" + state);
16296         
16297         this.active = state;
16298         if (!state) {
16299             this.el.removeClass('active');
16300             
16301         } else  if (!this.el.hasClass('active')) {
16302             this.el.addClass('active');
16303         }
16304         
16305         this.fireEvent('changed', this, state);
16306     }
16307     
16308     
16309 });
16310  
16311
16312  
16313
16314  /*
16315  * - LGPL
16316  *
16317  * DateField
16318  * 
16319  */
16320
16321 /**
16322  * @class Roo.bootstrap.DateField
16323  * @extends Roo.bootstrap.Input
16324  * Bootstrap DateField class
16325  * @cfg {Number} weekStart default 0
16326  * @cfg {String} viewMode default empty, (months|years)
16327  * @cfg {String} minViewMode default empty, (months|years)
16328  * @cfg {Number} startDate default -Infinity
16329  * @cfg {Number} endDate default Infinity
16330  * @cfg {Boolean} todayHighlight default false
16331  * @cfg {Boolean} todayBtn default false
16332  * @cfg {Boolean} calendarWeeks default false
16333  * @cfg {Object} daysOfWeekDisabled default empty
16334  * @cfg {Boolean} singleMode default false (true | false)
16335  * 
16336  * @cfg {Boolean} keyboardNavigation default true
16337  * @cfg {String} language default en
16338  * 
16339  * @constructor
16340  * Create a new DateField
16341  * @param {Object} config The config object
16342  */
16343
16344 Roo.bootstrap.DateField = function(config){
16345     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16346      this.addEvents({
16347             /**
16348              * @event show
16349              * Fires when this field show.
16350              * @param {Roo.bootstrap.DateField} this
16351              * @param {Mixed} date The date value
16352              */
16353             show : true,
16354             /**
16355              * @event show
16356              * Fires when this field hide.
16357              * @param {Roo.bootstrap.DateField} this
16358              * @param {Mixed} date The date value
16359              */
16360             hide : true,
16361             /**
16362              * @event select
16363              * Fires when select a date.
16364              * @param {Roo.bootstrap.DateField} this
16365              * @param {Mixed} date The date value
16366              */
16367             select : true,
16368             /**
16369              * @event beforeselect
16370              * Fires when before select a date.
16371              * @param {Roo.bootstrap.DateField} this
16372              * @param {Mixed} date The date value
16373              */
16374             beforeselect : true
16375         });
16376 };
16377
16378 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16379     
16380     /**
16381      * @cfg {String} format
16382      * The default date format string which can be overriden for localization support.  The format must be
16383      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16384      */
16385     format : "m/d/y",
16386     /**
16387      * @cfg {String} altFormats
16388      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16389      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16390      */
16391     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16392     
16393     weekStart : 0,
16394     
16395     viewMode : '',
16396     
16397     minViewMode : '',
16398     
16399     todayHighlight : false,
16400     
16401     todayBtn: false,
16402     
16403     language: 'en',
16404     
16405     keyboardNavigation: true,
16406     
16407     calendarWeeks: false,
16408     
16409     startDate: -Infinity,
16410     
16411     endDate: Infinity,
16412     
16413     daysOfWeekDisabled: [],
16414     
16415     _events: [],
16416     
16417     singleMode : false,
16418     
16419     UTCDate: function()
16420     {
16421         return new Date(Date.UTC.apply(Date, arguments));
16422     },
16423     
16424     UTCToday: function()
16425     {
16426         var today = new Date();
16427         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16428     },
16429     
16430     getDate: function() {
16431             var d = this.getUTCDate();
16432             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16433     },
16434     
16435     getUTCDate: function() {
16436             return this.date;
16437     },
16438     
16439     setDate: function(d) {
16440             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16441     },
16442     
16443     setUTCDate: function(d) {
16444             this.date = d;
16445             this.setValue(this.formatDate(this.date));
16446     },
16447         
16448     onRender: function(ct, position)
16449     {
16450         
16451         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16452         
16453         this.language = this.language || 'en';
16454         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16455         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16456         
16457         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16458         this.format = this.format || 'm/d/y';
16459         this.isInline = false;
16460         this.isInput = true;
16461         this.component = this.el.select('.add-on', true).first() || false;
16462         this.component = (this.component && this.component.length === 0) ? false : this.component;
16463         this.hasInput = this.component && this.inputEL().length;
16464         
16465         if (typeof(this.minViewMode === 'string')) {
16466             switch (this.minViewMode) {
16467                 case 'months':
16468                     this.minViewMode = 1;
16469                     break;
16470                 case 'years':
16471                     this.minViewMode = 2;
16472                     break;
16473                 default:
16474                     this.minViewMode = 0;
16475                     break;
16476             }
16477         }
16478         
16479         if (typeof(this.viewMode === 'string')) {
16480             switch (this.viewMode) {
16481                 case 'months':
16482                     this.viewMode = 1;
16483                     break;
16484                 case 'years':
16485                     this.viewMode = 2;
16486                     break;
16487                 default:
16488                     this.viewMode = 0;
16489                     break;
16490             }
16491         }
16492                 
16493         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16494         
16495 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16496         
16497         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16498         
16499         this.picker().on('mousedown', this.onMousedown, this);
16500         this.picker().on('click', this.onClick, this);
16501         
16502         this.picker().addClass('datepicker-dropdown');
16503         
16504         this.startViewMode = this.viewMode;
16505         
16506         if(this.singleMode){
16507             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16508                 v.setVisibilityMode(Roo.Element.DISPLAY)
16509                 v.hide();
16510             });
16511             
16512             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16513                 v.setStyle('width', '189px');
16514             });
16515         }
16516         
16517         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16518             if(!this.calendarWeeks){
16519                 v.remove();
16520                 return;
16521             }
16522             
16523             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16524             v.attr('colspan', function(i, val){
16525                 return parseInt(val) + 1;
16526             });
16527         })
16528                         
16529         
16530         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16531         
16532         this.setStartDate(this.startDate);
16533         this.setEndDate(this.endDate);
16534         
16535         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16536         
16537         this.fillDow();
16538         this.fillMonths();
16539         this.update();
16540         this.showMode();
16541         
16542         if(this.isInline) {
16543             this.show();
16544         }
16545     },
16546     
16547     picker : function()
16548     {
16549         return this.pickerEl;
16550 //        return this.el.select('.datepicker', true).first();
16551     },
16552     
16553     fillDow: function()
16554     {
16555         var dowCnt = this.weekStart;
16556         
16557         var dow = {
16558             tag: 'tr',
16559             cn: [
16560                 
16561             ]
16562         };
16563         
16564         if(this.calendarWeeks){
16565             dow.cn.push({
16566                 tag: 'th',
16567                 cls: 'cw',
16568                 html: '&nbsp;'
16569             })
16570         }
16571         
16572         while (dowCnt < this.weekStart + 7) {
16573             dow.cn.push({
16574                 tag: 'th',
16575                 cls: 'dow',
16576                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16577             });
16578         }
16579         
16580         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16581     },
16582     
16583     fillMonths: function()
16584     {    
16585         var i = 0;
16586         var months = this.picker().select('>.datepicker-months td', true).first();
16587         
16588         months.dom.innerHTML = '';
16589         
16590         while (i < 12) {
16591             var month = {
16592                 tag: 'span',
16593                 cls: 'month',
16594                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16595             };
16596             
16597             months.createChild(month);
16598         }
16599         
16600     },
16601     
16602     update: function()
16603     {
16604         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;
16605         
16606         if (this.date < this.startDate) {
16607             this.viewDate = new Date(this.startDate);
16608         } else if (this.date > this.endDate) {
16609             this.viewDate = new Date(this.endDate);
16610         } else {
16611             this.viewDate = new Date(this.date);
16612         }
16613         
16614         this.fill();
16615     },
16616     
16617     fill: function() 
16618     {
16619         var d = new Date(this.viewDate),
16620                 year = d.getUTCFullYear(),
16621                 month = d.getUTCMonth(),
16622                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16623                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16624                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16625                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16626                 currentDate = this.date && this.date.valueOf(),
16627                 today = this.UTCToday();
16628         
16629         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16630         
16631 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16632         
16633 //        this.picker.select('>tfoot th.today').
16634 //                                              .text(dates[this.language].today)
16635 //                                              .toggle(this.todayBtn !== false);
16636     
16637         this.updateNavArrows();
16638         this.fillMonths();
16639                                                 
16640         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16641         
16642         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16643          
16644         prevMonth.setUTCDate(day);
16645         
16646         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16647         
16648         var nextMonth = new Date(prevMonth);
16649         
16650         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16651         
16652         nextMonth = nextMonth.valueOf();
16653         
16654         var fillMonths = false;
16655         
16656         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16657         
16658         while(prevMonth.valueOf() < nextMonth) {
16659             var clsName = '';
16660             
16661             if (prevMonth.getUTCDay() === this.weekStart) {
16662                 if(fillMonths){
16663                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16664                 }
16665                     
16666                 fillMonths = {
16667                     tag: 'tr',
16668                     cn: []
16669                 };
16670                 
16671                 if(this.calendarWeeks){
16672                     // ISO 8601: First week contains first thursday.
16673                     // ISO also states week starts on Monday, but we can be more abstract here.
16674                     var
16675                     // Start of current week: based on weekstart/current date
16676                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16677                     // Thursday of this week
16678                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16679                     // First Thursday of year, year from thursday
16680                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16681                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16682                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16683                     
16684                     fillMonths.cn.push({
16685                         tag: 'td',
16686                         cls: 'cw',
16687                         html: calWeek
16688                     });
16689                 }
16690             }
16691             
16692             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16693                 clsName += ' old';
16694             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16695                 clsName += ' new';
16696             }
16697             if (this.todayHighlight &&
16698                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16699                 prevMonth.getUTCMonth() == today.getMonth() &&
16700                 prevMonth.getUTCDate() == today.getDate()) {
16701                 clsName += ' today';
16702             }
16703             
16704             if (currentDate && prevMonth.valueOf() === currentDate) {
16705                 clsName += ' active';
16706             }
16707             
16708             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16709                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16710                     clsName += ' disabled';
16711             }
16712             
16713             fillMonths.cn.push({
16714                 tag: 'td',
16715                 cls: 'day ' + clsName,
16716                 html: prevMonth.getDate()
16717             })
16718             
16719             prevMonth.setDate(prevMonth.getDate()+1);
16720         }
16721           
16722         var currentYear = this.date && this.date.getUTCFullYear();
16723         var currentMonth = this.date && this.date.getUTCMonth();
16724         
16725         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16726         
16727         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16728             v.removeClass('active');
16729             
16730             if(currentYear === year && k === currentMonth){
16731                 v.addClass('active');
16732             }
16733             
16734             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16735                 v.addClass('disabled');
16736             }
16737             
16738         });
16739         
16740         
16741         year = parseInt(year/10, 10) * 10;
16742         
16743         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16744         
16745         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16746         
16747         year -= 1;
16748         for (var i = -1; i < 11; i++) {
16749             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16750                 tag: 'span',
16751                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16752                 html: year
16753             })
16754             
16755             year += 1;
16756         }
16757     },
16758     
16759     showMode: function(dir) 
16760     {
16761         if (dir) {
16762             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16763         }
16764         
16765         Roo.each(this.picker().select('>div',true).elements, function(v){
16766             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16767             v.hide();
16768         });
16769         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16770     },
16771     
16772     place: function()
16773     {
16774         if(this.isInline) return;
16775         
16776         this.picker().removeClass(['bottom', 'top']);
16777         
16778         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16779             /*
16780              * place to the top of element!
16781              *
16782              */
16783             
16784             this.picker().addClass('top');
16785             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16786             
16787             return;
16788         }
16789         
16790         this.picker().addClass('bottom');
16791         
16792         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16793     },
16794     
16795     parseDate : function(value)
16796     {
16797         if(!value || value instanceof Date){
16798             return value;
16799         }
16800         var v = Date.parseDate(value, this.format);
16801         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16802             v = Date.parseDate(value, 'Y-m-d');
16803         }
16804         if(!v && this.altFormats){
16805             if(!this.altFormatsArray){
16806                 this.altFormatsArray = this.altFormats.split("|");
16807             }
16808             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16809                 v = Date.parseDate(value, this.altFormatsArray[i]);
16810             }
16811         }
16812         return v;
16813     },
16814     
16815     formatDate : function(date, fmt)
16816     {   
16817         return (!date || !(date instanceof Date)) ?
16818         date : date.dateFormat(fmt || this.format);
16819     },
16820     
16821     onFocus : function()
16822     {
16823         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16824         this.show();
16825     },
16826     
16827     onBlur : function()
16828     {
16829         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16830         
16831         var d = this.inputEl().getValue();
16832         
16833         this.setValue(d);
16834                 
16835         this.hide();
16836     },
16837     
16838     show : function()
16839     {
16840         this.picker().show();
16841         this.update();
16842         this.place();
16843         
16844         this.fireEvent('show', this, this.date);
16845     },
16846     
16847     hide : function()
16848     {
16849         if(this.isInline) return;
16850         this.picker().hide();
16851         this.viewMode = this.startViewMode;
16852         this.showMode();
16853         
16854         this.fireEvent('hide', this, this.date);
16855         
16856     },
16857     
16858     onMousedown: function(e)
16859     {
16860         e.stopPropagation();
16861         e.preventDefault();
16862     },
16863     
16864     keyup: function(e)
16865     {
16866         Roo.bootstrap.DateField.superclass.keyup.call(this);
16867         this.update();
16868     },
16869
16870     setValue: function(v)
16871     {
16872         if(this.fireEvent('beforeselect', this, v) !== false){
16873             var d = new Date(this.parseDate(v) ).clearTime();
16874         
16875             if(isNaN(d.getTime())){
16876                 this.date = this.viewDate = '';
16877                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16878                 return;
16879             }
16880
16881             v = this.formatDate(d);
16882
16883             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16884
16885             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16886
16887             this.update();
16888
16889             this.fireEvent('select', this, this.date);
16890         }
16891     },
16892     
16893     getValue: function()
16894     {
16895         return this.formatDate(this.date);
16896     },
16897     
16898     fireKey: function(e)
16899     {
16900         if (!this.picker().isVisible()){
16901             if (e.keyCode == 27) // allow escape to hide and re-show picker
16902                 this.show();
16903             return;
16904         }
16905         
16906         var dateChanged = false,
16907         dir, day, month,
16908         newDate, newViewDate;
16909         
16910         switch(e.keyCode){
16911             case 27: // escape
16912                 this.hide();
16913                 e.preventDefault();
16914                 break;
16915             case 37: // left
16916             case 39: // right
16917                 if (!this.keyboardNavigation) break;
16918                 dir = e.keyCode == 37 ? -1 : 1;
16919                 
16920                 if (e.ctrlKey){
16921                     newDate = this.moveYear(this.date, dir);
16922                     newViewDate = this.moveYear(this.viewDate, dir);
16923                 } else if (e.shiftKey){
16924                     newDate = this.moveMonth(this.date, dir);
16925                     newViewDate = this.moveMonth(this.viewDate, dir);
16926                 } else {
16927                     newDate = new Date(this.date);
16928                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16929                     newViewDate = new Date(this.viewDate);
16930                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16931                 }
16932                 if (this.dateWithinRange(newDate)){
16933                     this.date = newDate;
16934                     this.viewDate = newViewDate;
16935                     this.setValue(this.formatDate(this.date));
16936 //                    this.update();
16937                     e.preventDefault();
16938                     dateChanged = true;
16939                 }
16940                 break;
16941             case 38: // up
16942             case 40: // down
16943                 if (!this.keyboardNavigation) break;
16944                 dir = e.keyCode == 38 ? -1 : 1;
16945                 if (e.ctrlKey){
16946                     newDate = this.moveYear(this.date, dir);
16947                     newViewDate = this.moveYear(this.viewDate, dir);
16948                 } else if (e.shiftKey){
16949                     newDate = this.moveMonth(this.date, dir);
16950                     newViewDate = this.moveMonth(this.viewDate, dir);
16951                 } else {
16952                     newDate = new Date(this.date);
16953                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16954                     newViewDate = new Date(this.viewDate);
16955                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16956                 }
16957                 if (this.dateWithinRange(newDate)){
16958                     this.date = newDate;
16959                     this.viewDate = newViewDate;
16960                     this.setValue(this.formatDate(this.date));
16961 //                    this.update();
16962                     e.preventDefault();
16963                     dateChanged = true;
16964                 }
16965                 break;
16966             case 13: // enter
16967                 this.setValue(this.formatDate(this.date));
16968                 this.hide();
16969                 e.preventDefault();
16970                 break;
16971             case 9: // tab
16972                 this.setValue(this.formatDate(this.date));
16973                 this.hide();
16974                 break;
16975             case 16: // shift
16976             case 17: // ctrl
16977             case 18: // alt
16978                 break;
16979             default :
16980                 this.hide();
16981                 
16982         }
16983     },
16984     
16985     
16986     onClick: function(e) 
16987     {
16988         e.stopPropagation();
16989         e.preventDefault();
16990         
16991         var target = e.getTarget();
16992         
16993         if(target.nodeName.toLowerCase() === 'i'){
16994             target = Roo.get(target).dom.parentNode;
16995         }
16996         
16997         var nodeName = target.nodeName;
16998         var className = target.className;
16999         var html = target.innerHTML;
17000         //Roo.log(nodeName);
17001         
17002         switch(nodeName.toLowerCase()) {
17003             case 'th':
17004                 switch(className) {
17005                     case 'switch':
17006                         this.showMode(1);
17007                         break;
17008                     case 'prev':
17009                     case 'next':
17010                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17011                         switch(this.viewMode){
17012                                 case 0:
17013                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17014                                         break;
17015                                 case 1:
17016                                 case 2:
17017                                         this.viewDate = this.moveYear(this.viewDate, dir);
17018                                         break;
17019                         }
17020                         this.fill();
17021                         break;
17022                     case 'today':
17023                         var date = new Date();
17024                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17025 //                        this.fill()
17026                         this.setValue(this.formatDate(this.date));
17027                         
17028                         this.hide();
17029                         break;
17030                 }
17031                 break;
17032             case 'span':
17033                 if (className.indexOf('disabled') < 0) {
17034                     this.viewDate.setUTCDate(1);
17035                     if (className.indexOf('month') > -1) {
17036                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17037                     } else {
17038                         var year = parseInt(html, 10) || 0;
17039                         this.viewDate.setUTCFullYear(year);
17040                         
17041                     }
17042                     
17043                     if(this.singleMode){
17044                         this.setValue(this.formatDate(this.viewDate));
17045                         this.hide();
17046                         return;
17047                     }
17048                     
17049                     this.showMode(-1);
17050                     this.fill();
17051                 }
17052                 break;
17053                 
17054             case 'td':
17055                 //Roo.log(className);
17056                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17057                     var day = parseInt(html, 10) || 1;
17058                     var year = this.viewDate.getUTCFullYear(),
17059                         month = this.viewDate.getUTCMonth();
17060
17061                     if (className.indexOf('old') > -1) {
17062                         if(month === 0 ){
17063                             month = 11;
17064                             year -= 1;
17065                         }else{
17066                             month -= 1;
17067                         }
17068                     } else if (className.indexOf('new') > -1) {
17069                         if (month == 11) {
17070                             month = 0;
17071                             year += 1;
17072                         } else {
17073                             month += 1;
17074                         }
17075                     }
17076                     //Roo.log([year,month,day]);
17077                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17078                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17079 //                    this.fill();
17080                     //Roo.log(this.formatDate(this.date));
17081                     this.setValue(this.formatDate(this.date));
17082                     this.hide();
17083                 }
17084                 break;
17085         }
17086     },
17087     
17088     setStartDate: function(startDate)
17089     {
17090         this.startDate = startDate || -Infinity;
17091         if (this.startDate !== -Infinity) {
17092             this.startDate = this.parseDate(this.startDate);
17093         }
17094         this.update();
17095         this.updateNavArrows();
17096     },
17097
17098     setEndDate: function(endDate)
17099     {
17100         this.endDate = endDate || Infinity;
17101         if (this.endDate !== Infinity) {
17102             this.endDate = this.parseDate(this.endDate);
17103         }
17104         this.update();
17105         this.updateNavArrows();
17106     },
17107     
17108     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17109     {
17110         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17111         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17112             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17113         }
17114         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17115             return parseInt(d, 10);
17116         });
17117         this.update();
17118         this.updateNavArrows();
17119     },
17120     
17121     updateNavArrows: function() 
17122     {
17123         if(this.singleMode){
17124             return;
17125         }
17126         
17127         var d = new Date(this.viewDate),
17128         year = d.getUTCFullYear(),
17129         month = d.getUTCMonth();
17130         
17131         Roo.each(this.picker().select('.prev', true).elements, function(v){
17132             v.show();
17133             switch (this.viewMode) {
17134                 case 0:
17135
17136                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17137                         v.hide();
17138                     }
17139                     break;
17140                 case 1:
17141                 case 2:
17142                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17143                         v.hide();
17144                     }
17145                     break;
17146             }
17147         });
17148         
17149         Roo.each(this.picker().select('.next', true).elements, function(v){
17150             v.show();
17151             switch (this.viewMode) {
17152                 case 0:
17153
17154                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17155                         v.hide();
17156                     }
17157                     break;
17158                 case 1:
17159                 case 2:
17160                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17161                         v.hide();
17162                     }
17163                     break;
17164             }
17165         })
17166     },
17167     
17168     moveMonth: function(date, dir)
17169     {
17170         if (!dir) return date;
17171         var new_date = new Date(date.valueOf()),
17172         day = new_date.getUTCDate(),
17173         month = new_date.getUTCMonth(),
17174         mag = Math.abs(dir),
17175         new_month, test;
17176         dir = dir > 0 ? 1 : -1;
17177         if (mag == 1){
17178             test = dir == -1
17179             // If going back one month, make sure month is not current month
17180             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17181             ? function(){
17182                 return new_date.getUTCMonth() == month;
17183             }
17184             // If going forward one month, make sure month is as expected
17185             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17186             : function(){
17187                 return new_date.getUTCMonth() != new_month;
17188             };
17189             new_month = month + dir;
17190             new_date.setUTCMonth(new_month);
17191             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17192             if (new_month < 0 || new_month > 11)
17193                 new_month = (new_month + 12) % 12;
17194         } else {
17195             // For magnitudes >1, move one month at a time...
17196             for (var i=0; i<mag; i++)
17197                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17198                 new_date = this.moveMonth(new_date, dir);
17199             // ...then reset the day, keeping it in the new month
17200             new_month = new_date.getUTCMonth();
17201             new_date.setUTCDate(day);
17202             test = function(){
17203                 return new_month != new_date.getUTCMonth();
17204             };
17205         }
17206         // Common date-resetting loop -- if date is beyond end of month, make it
17207         // end of month
17208         while (test()){
17209             new_date.setUTCDate(--day);
17210             new_date.setUTCMonth(new_month);
17211         }
17212         return new_date;
17213     },
17214
17215     moveYear: function(date, dir)
17216     {
17217         return this.moveMonth(date, dir*12);
17218     },
17219
17220     dateWithinRange: function(date)
17221     {
17222         return date >= this.startDate && date <= this.endDate;
17223     },
17224
17225     
17226     remove: function() 
17227     {
17228         this.picker().remove();
17229     }
17230    
17231 });
17232
17233 Roo.apply(Roo.bootstrap.DateField,  {
17234     
17235     head : {
17236         tag: 'thead',
17237         cn: [
17238         {
17239             tag: 'tr',
17240             cn: [
17241             {
17242                 tag: 'th',
17243                 cls: 'prev',
17244                 html: '<i class="fa fa-arrow-left"/>'
17245             },
17246             {
17247                 tag: 'th',
17248                 cls: 'switch',
17249                 colspan: '5'
17250             },
17251             {
17252                 tag: 'th',
17253                 cls: 'next',
17254                 html: '<i class="fa fa-arrow-right"/>'
17255             }
17256
17257             ]
17258         }
17259         ]
17260     },
17261     
17262     content : {
17263         tag: 'tbody',
17264         cn: [
17265         {
17266             tag: 'tr',
17267             cn: [
17268             {
17269                 tag: 'td',
17270                 colspan: '7'
17271             }
17272             ]
17273         }
17274         ]
17275     },
17276     
17277     footer : {
17278         tag: 'tfoot',
17279         cn: [
17280         {
17281             tag: 'tr',
17282             cn: [
17283             {
17284                 tag: 'th',
17285                 colspan: '7',
17286                 cls: 'today'
17287             }
17288                     
17289             ]
17290         }
17291         ]
17292     },
17293     
17294     dates:{
17295         en: {
17296             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17297             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17298             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17299             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17300             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17301             today: "Today"
17302         }
17303     },
17304     
17305     modes: [
17306     {
17307         clsName: 'days',
17308         navFnc: 'Month',
17309         navStep: 1
17310     },
17311     {
17312         clsName: 'months',
17313         navFnc: 'FullYear',
17314         navStep: 1
17315     },
17316     {
17317         clsName: 'years',
17318         navFnc: 'FullYear',
17319         navStep: 10
17320     }]
17321 });
17322
17323 Roo.apply(Roo.bootstrap.DateField,  {
17324   
17325     template : {
17326         tag: 'div',
17327         cls: 'datepicker dropdown-menu roo-dynamic',
17328         cn: [
17329         {
17330             tag: 'div',
17331             cls: 'datepicker-days',
17332             cn: [
17333             {
17334                 tag: 'table',
17335                 cls: 'table-condensed',
17336                 cn:[
17337                 Roo.bootstrap.DateField.head,
17338                 {
17339                     tag: 'tbody'
17340                 },
17341                 Roo.bootstrap.DateField.footer
17342                 ]
17343             }
17344             ]
17345         },
17346         {
17347             tag: 'div',
17348             cls: 'datepicker-months',
17349             cn: [
17350             {
17351                 tag: 'table',
17352                 cls: 'table-condensed',
17353                 cn:[
17354                 Roo.bootstrap.DateField.head,
17355                 Roo.bootstrap.DateField.content,
17356                 Roo.bootstrap.DateField.footer
17357                 ]
17358             }
17359             ]
17360         },
17361         {
17362             tag: 'div',
17363             cls: 'datepicker-years',
17364             cn: [
17365             {
17366                 tag: 'table',
17367                 cls: 'table-condensed',
17368                 cn:[
17369                 Roo.bootstrap.DateField.head,
17370                 Roo.bootstrap.DateField.content,
17371                 Roo.bootstrap.DateField.footer
17372                 ]
17373             }
17374             ]
17375         }
17376         ]
17377     }
17378 });
17379
17380  
17381
17382  /*
17383  * - LGPL
17384  *
17385  * TimeField
17386  * 
17387  */
17388
17389 /**
17390  * @class Roo.bootstrap.TimeField
17391  * @extends Roo.bootstrap.Input
17392  * Bootstrap DateField class
17393  * 
17394  * 
17395  * @constructor
17396  * Create a new TimeField
17397  * @param {Object} config The config object
17398  */
17399
17400 Roo.bootstrap.TimeField = function(config){
17401     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17402     this.addEvents({
17403             /**
17404              * @event show
17405              * Fires when this field show.
17406              * @param {Roo.bootstrap.DateField} thisthis
17407              * @param {Mixed} date The date value
17408              */
17409             show : true,
17410             /**
17411              * @event show
17412              * Fires when this field hide.
17413              * @param {Roo.bootstrap.DateField} this
17414              * @param {Mixed} date The date value
17415              */
17416             hide : true,
17417             /**
17418              * @event select
17419              * Fires when select a date.
17420              * @param {Roo.bootstrap.DateField} this
17421              * @param {Mixed} date The date value
17422              */
17423             select : true
17424         });
17425 };
17426
17427 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17428     
17429     /**
17430      * @cfg {String} format
17431      * The default time format string which can be overriden for localization support.  The format must be
17432      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17433      */
17434     format : "H:i",
17435        
17436     onRender: function(ct, position)
17437     {
17438         
17439         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17440                 
17441         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17442         
17443         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17444         
17445         this.pop = this.picker().select('>.datepicker-time',true).first();
17446         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17447         
17448         this.picker().on('mousedown', this.onMousedown, this);
17449         this.picker().on('click', this.onClick, this);
17450         
17451         this.picker().addClass('datepicker-dropdown');
17452     
17453         this.fillTime();
17454         this.update();
17455             
17456         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17457         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17458         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17459         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17460         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17461         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17462
17463     },
17464     
17465     fireKey: function(e){
17466         if (!this.picker().isVisible()){
17467             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17468                 this.show();
17469             }
17470             return;
17471         }
17472
17473         e.preventDefault();
17474         
17475         switch(e.keyCode){
17476             case 27: // escape
17477                 this.hide();
17478                 break;
17479             case 37: // left
17480             case 39: // right
17481                 this.onTogglePeriod();
17482                 break;
17483             case 38: // up
17484                 this.onIncrementMinutes();
17485                 break;
17486             case 40: // down
17487                 this.onDecrementMinutes();
17488                 break;
17489             case 13: // enter
17490             case 9: // tab
17491                 this.setTime();
17492                 break;
17493         }
17494     },
17495     
17496     onClick: function(e) {
17497         e.stopPropagation();
17498         e.preventDefault();
17499     },
17500     
17501     picker : function()
17502     {
17503         return this.el.select('.datepicker', true).first();
17504     },
17505     
17506     fillTime: function()
17507     {    
17508         var time = this.pop.select('tbody', true).first();
17509         
17510         time.dom.innerHTML = '';
17511         
17512         time.createChild({
17513             tag: 'tr',
17514             cn: [
17515                 {
17516                     tag: 'td',
17517                     cn: [
17518                         {
17519                             tag: 'a',
17520                             href: '#',
17521                             cls: 'btn',
17522                             cn: [
17523                                 {
17524                                     tag: 'span',
17525                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17526                                 }
17527                             ]
17528                         } 
17529                     ]
17530                 },
17531                 {
17532                     tag: 'td',
17533                     cls: 'separator'
17534                 },
17535                 {
17536                     tag: 'td',
17537                     cn: [
17538                         {
17539                             tag: 'a',
17540                             href: '#',
17541                             cls: 'btn',
17542                             cn: [
17543                                 {
17544                                     tag: 'span',
17545                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17546                                 }
17547                             ]
17548                         }
17549                     ]
17550                 },
17551                 {
17552                     tag: 'td',
17553                     cls: 'separator'
17554                 }
17555             ]
17556         });
17557         
17558         time.createChild({
17559             tag: 'tr',
17560             cn: [
17561                 {
17562                     tag: 'td',
17563                     cn: [
17564                         {
17565                             tag: 'span',
17566                             cls: 'timepicker-hour',
17567                             html: '00'
17568                         }  
17569                     ]
17570                 },
17571                 {
17572                     tag: 'td',
17573                     cls: 'separator',
17574                     html: ':'
17575                 },
17576                 {
17577                     tag: 'td',
17578                     cn: [
17579                         {
17580                             tag: 'span',
17581                             cls: 'timepicker-minute',
17582                             html: '00'
17583                         }  
17584                     ]
17585                 },
17586                 {
17587                     tag: 'td',
17588                     cls: 'separator'
17589                 },
17590                 {
17591                     tag: 'td',
17592                     cn: [
17593                         {
17594                             tag: 'button',
17595                             type: 'button',
17596                             cls: 'btn btn-primary period',
17597                             html: 'AM'
17598                             
17599                         }
17600                     ]
17601                 }
17602             ]
17603         });
17604         
17605         time.createChild({
17606             tag: 'tr',
17607             cn: [
17608                 {
17609                     tag: 'td',
17610                     cn: [
17611                         {
17612                             tag: 'a',
17613                             href: '#',
17614                             cls: 'btn',
17615                             cn: [
17616                                 {
17617                                     tag: 'span',
17618                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17619                                 }
17620                             ]
17621                         }
17622                     ]
17623                 },
17624                 {
17625                     tag: 'td',
17626                     cls: 'separator'
17627                 },
17628                 {
17629                     tag: 'td',
17630                     cn: [
17631                         {
17632                             tag: 'a',
17633                             href: '#',
17634                             cls: 'btn',
17635                             cn: [
17636                                 {
17637                                     tag: 'span',
17638                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17639                                 }
17640                             ]
17641                         }
17642                     ]
17643                 },
17644                 {
17645                     tag: 'td',
17646                     cls: 'separator'
17647                 }
17648             ]
17649         });
17650         
17651     },
17652     
17653     update: function()
17654     {
17655         
17656         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17657         
17658         this.fill();
17659     },
17660     
17661     fill: function() 
17662     {
17663         var hours = this.time.getHours();
17664         var minutes = this.time.getMinutes();
17665         var period = 'AM';
17666         
17667         if(hours > 11){
17668             period = 'PM';
17669         }
17670         
17671         if(hours == 0){
17672             hours = 12;
17673         }
17674         
17675         
17676         if(hours > 12){
17677             hours = hours - 12;
17678         }
17679         
17680         if(hours < 10){
17681             hours = '0' + hours;
17682         }
17683         
17684         if(minutes < 10){
17685             minutes = '0' + minutes;
17686         }
17687         
17688         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17689         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17690         this.pop.select('button', true).first().dom.innerHTML = period;
17691         
17692     },
17693     
17694     place: function()
17695     {   
17696         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17697         
17698         var cls = ['bottom'];
17699         
17700         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17701             cls.pop();
17702             cls.push('top');
17703         }
17704         
17705         cls.push('right');
17706         
17707         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17708             cls.pop();
17709             cls.push('left');
17710         }
17711         
17712         this.picker().addClass(cls.join('-'));
17713         
17714         var _this = this;
17715         
17716         Roo.each(cls, function(c){
17717             if(c == 'bottom'){
17718                 _this.picker().setTop(_this.inputEl().getHeight());
17719                 return;
17720             }
17721             if(c == 'top'){
17722                 _this.picker().setTop(0 - _this.picker().getHeight());
17723                 return;
17724             }
17725             
17726             if(c == 'left'){
17727                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17728                 return;
17729             }
17730             if(c == 'right'){
17731                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17732                 return;
17733             }
17734         });
17735         
17736     },
17737   
17738     onFocus : function()
17739     {
17740         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17741         this.show();
17742     },
17743     
17744     onBlur : function()
17745     {
17746         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17747         this.hide();
17748     },
17749     
17750     show : function()
17751     {
17752         this.picker().show();
17753         this.pop.show();
17754         this.update();
17755         this.place();
17756         
17757         this.fireEvent('show', this, this.date);
17758     },
17759     
17760     hide : function()
17761     {
17762         this.picker().hide();
17763         this.pop.hide();
17764         
17765         this.fireEvent('hide', this, this.date);
17766     },
17767     
17768     setTime : function()
17769     {
17770         this.hide();
17771         this.setValue(this.time.format(this.format));
17772         
17773         this.fireEvent('select', this, this.date);
17774         
17775         
17776     },
17777     
17778     onMousedown: function(e){
17779         e.stopPropagation();
17780         e.preventDefault();
17781     },
17782     
17783     onIncrementHours: function()
17784     {
17785         Roo.log('onIncrementHours');
17786         this.time = this.time.add(Date.HOUR, 1);
17787         this.update();
17788         
17789     },
17790     
17791     onDecrementHours: function()
17792     {
17793         Roo.log('onDecrementHours');
17794         this.time = this.time.add(Date.HOUR, -1);
17795         this.update();
17796     },
17797     
17798     onIncrementMinutes: function()
17799     {
17800         Roo.log('onIncrementMinutes');
17801         this.time = this.time.add(Date.MINUTE, 1);
17802         this.update();
17803     },
17804     
17805     onDecrementMinutes: function()
17806     {
17807         Roo.log('onDecrementMinutes');
17808         this.time = this.time.add(Date.MINUTE, -1);
17809         this.update();
17810     },
17811     
17812     onTogglePeriod: function()
17813     {
17814         Roo.log('onTogglePeriod');
17815         this.time = this.time.add(Date.HOUR, 12);
17816         this.update();
17817     }
17818     
17819    
17820 });
17821
17822 Roo.apply(Roo.bootstrap.TimeField,  {
17823     
17824     content : {
17825         tag: 'tbody',
17826         cn: [
17827             {
17828                 tag: 'tr',
17829                 cn: [
17830                 {
17831                     tag: 'td',
17832                     colspan: '7'
17833                 }
17834                 ]
17835             }
17836         ]
17837     },
17838     
17839     footer : {
17840         tag: 'tfoot',
17841         cn: [
17842             {
17843                 tag: 'tr',
17844                 cn: [
17845                 {
17846                     tag: 'th',
17847                     colspan: '7',
17848                     cls: '',
17849                     cn: [
17850                         {
17851                             tag: 'button',
17852                             cls: 'btn btn-info ok',
17853                             html: 'OK'
17854                         }
17855                     ]
17856                 }
17857
17858                 ]
17859             }
17860         ]
17861     }
17862 });
17863
17864 Roo.apply(Roo.bootstrap.TimeField,  {
17865   
17866     template : {
17867         tag: 'div',
17868         cls: 'datepicker dropdown-menu',
17869         cn: [
17870             {
17871                 tag: 'div',
17872                 cls: 'datepicker-time',
17873                 cn: [
17874                 {
17875                     tag: 'table',
17876                     cls: 'table-condensed',
17877                     cn:[
17878                     Roo.bootstrap.TimeField.content,
17879                     Roo.bootstrap.TimeField.footer
17880                     ]
17881                 }
17882                 ]
17883             }
17884         ]
17885     }
17886 });
17887
17888  
17889
17890  /*
17891  * - LGPL
17892  *
17893  * MonthField
17894  * 
17895  */
17896
17897 /**
17898  * @class Roo.bootstrap.MonthField
17899  * @extends Roo.bootstrap.Input
17900  * Bootstrap MonthField class
17901  * 
17902  * @cfg {String} language default en
17903  * 
17904  * @constructor
17905  * Create a new MonthField
17906  * @param {Object} config The config object
17907  */
17908
17909 Roo.bootstrap.MonthField = function(config){
17910     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17911     
17912     this.addEvents({
17913         /**
17914          * @event show
17915          * Fires when this field show.
17916          * @param {Roo.bootstrap.MonthField} this
17917          * @param {Mixed} date The date value
17918          */
17919         show : true,
17920         /**
17921          * @event show
17922          * Fires when this field hide.
17923          * @param {Roo.bootstrap.MonthField} this
17924          * @param {Mixed} date The date value
17925          */
17926         hide : true,
17927         /**
17928          * @event select
17929          * Fires when select a date.
17930          * @param {Roo.bootstrap.MonthField} this
17931          * @param {String} oldvalue The old value
17932          * @param {String} newvalue The new value
17933          */
17934         select : true
17935     });
17936 };
17937
17938 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17939     
17940     onRender: function(ct, position)
17941     {
17942         
17943         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17944         
17945         this.language = this.language || 'en';
17946         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17947         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17948         
17949         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17950         this.isInline = false;
17951         this.isInput = true;
17952         this.component = this.el.select('.add-on', true).first() || false;
17953         this.component = (this.component && this.component.length === 0) ? false : this.component;
17954         this.hasInput = this.component && this.inputEL().length;
17955         
17956         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17957         
17958         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17959         
17960         this.picker().on('mousedown', this.onMousedown, this);
17961         this.picker().on('click', this.onClick, this);
17962         
17963         this.picker().addClass('datepicker-dropdown');
17964         
17965         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17966             v.setStyle('width', '189px');
17967         });
17968         
17969         this.fillMonths();
17970         
17971         this.update();
17972         
17973         if(this.isInline) {
17974             this.show();
17975         }
17976         
17977     },
17978     
17979     setValue: function(v, suppressEvent)
17980     {   
17981         var o = this.getValue();
17982         
17983         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17984         
17985         this.update();
17986
17987         if(suppressEvent !== true){
17988             this.fireEvent('select', this, o, v);
17989         }
17990         
17991     },
17992     
17993     getValue: function()
17994     {
17995         return this.value;
17996     },
17997     
17998     onClick: function(e) 
17999     {
18000         e.stopPropagation();
18001         e.preventDefault();
18002         
18003         var target = e.getTarget();
18004         
18005         if(target.nodeName.toLowerCase() === 'i'){
18006             target = Roo.get(target).dom.parentNode;
18007         }
18008         
18009         var nodeName = target.nodeName;
18010         var className = target.className;
18011         var html = target.innerHTML;
18012         
18013         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18014             return;
18015         }
18016         
18017         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18018         
18019         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18020         
18021         this.hide();
18022                         
18023     },
18024     
18025     picker : function()
18026     {
18027         return this.pickerEl;
18028     },
18029     
18030     fillMonths: function()
18031     {    
18032         var i = 0;
18033         var months = this.picker().select('>.datepicker-months td', true).first();
18034         
18035         months.dom.innerHTML = '';
18036         
18037         while (i < 12) {
18038             var month = {
18039                 tag: 'span',
18040                 cls: 'month',
18041                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18042             };
18043             
18044             months.createChild(month);
18045         }
18046         
18047     },
18048     
18049     update: function()
18050     {
18051         var _this = this;
18052         
18053         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18054             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18055         }
18056         
18057         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18058             e.removeClass('active');
18059             
18060             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18061                 e.addClass('active');
18062             }
18063         })
18064     },
18065     
18066     place: function()
18067     {
18068         if(this.isInline) return;
18069         
18070         this.picker().removeClass(['bottom', 'top']);
18071         
18072         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18073             /*
18074              * place to the top of element!
18075              *
18076              */
18077             
18078             this.picker().addClass('top');
18079             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18080             
18081             return;
18082         }
18083         
18084         this.picker().addClass('bottom');
18085         
18086         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18087     },
18088     
18089     onFocus : function()
18090     {
18091         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18092         this.show();
18093     },
18094     
18095     onBlur : function()
18096     {
18097         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18098         
18099         var d = this.inputEl().getValue();
18100         
18101         this.setValue(d);
18102                 
18103         this.hide();
18104     },
18105     
18106     show : function()
18107     {
18108         this.picker().show();
18109         this.picker().select('>.datepicker-months', true).first().show();
18110         this.update();
18111         this.place();
18112         
18113         this.fireEvent('show', this, this.date);
18114     },
18115     
18116     hide : function()
18117     {
18118         if(this.isInline) return;
18119         this.picker().hide();
18120         this.fireEvent('hide', this, this.date);
18121         
18122     },
18123     
18124     onMousedown: function(e)
18125     {
18126         e.stopPropagation();
18127         e.preventDefault();
18128     },
18129     
18130     keyup: function(e)
18131     {
18132         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18133         this.update();
18134     },
18135
18136     fireKey: function(e)
18137     {
18138         if (!this.picker().isVisible()){
18139             if (e.keyCode == 27) // allow escape to hide and re-show picker
18140                 this.show();
18141             return;
18142         }
18143         
18144         var dir;
18145         
18146         switch(e.keyCode){
18147             case 27: // escape
18148                 this.hide();
18149                 e.preventDefault();
18150                 break;
18151             case 37: // left
18152             case 39: // right
18153                 dir = e.keyCode == 37 ? -1 : 1;
18154                 
18155                 this.vIndex = this.vIndex + dir;
18156                 
18157                 if(this.vIndex < 0){
18158                     this.vIndex = 0;
18159                 }
18160                 
18161                 if(this.vIndex > 11){
18162                     this.vIndex = 11;
18163                 }
18164                 
18165                 if(isNaN(this.vIndex)){
18166                     this.vIndex = 0;
18167                 }
18168                 
18169                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18170                 
18171                 break;
18172             case 38: // up
18173             case 40: // down
18174                 
18175                 dir = e.keyCode == 38 ? -1 : 1;
18176                 
18177                 this.vIndex = this.vIndex + dir * 4;
18178                 
18179                 if(this.vIndex < 0){
18180                     this.vIndex = 0;
18181                 }
18182                 
18183                 if(this.vIndex > 11){
18184                     this.vIndex = 11;
18185                 }
18186                 
18187                 if(isNaN(this.vIndex)){
18188                     this.vIndex = 0;
18189                 }
18190                 
18191                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18192                 break;
18193                 
18194             case 13: // enter
18195                 
18196                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18197                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18198                 }
18199                 
18200                 this.hide();
18201                 e.preventDefault();
18202                 break;
18203             case 9: // tab
18204                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18205                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18206                 }
18207                 this.hide();
18208                 break;
18209             case 16: // shift
18210             case 17: // ctrl
18211             case 18: // alt
18212                 break;
18213             default :
18214                 this.hide();
18215                 
18216         }
18217     },
18218     
18219     remove: function() 
18220     {
18221         this.picker().remove();
18222     }
18223    
18224 });
18225
18226 Roo.apply(Roo.bootstrap.MonthField,  {
18227     
18228     content : {
18229         tag: 'tbody',
18230         cn: [
18231         {
18232             tag: 'tr',
18233             cn: [
18234             {
18235                 tag: 'td',
18236                 colspan: '7'
18237             }
18238             ]
18239         }
18240         ]
18241     },
18242     
18243     dates:{
18244         en: {
18245             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18246             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18247         }
18248     }
18249 });
18250
18251 Roo.apply(Roo.bootstrap.MonthField,  {
18252   
18253     template : {
18254         tag: 'div',
18255         cls: 'datepicker dropdown-menu roo-dynamic',
18256         cn: [
18257             {
18258                 tag: 'div',
18259                 cls: 'datepicker-months',
18260                 cn: [
18261                 {
18262                     tag: 'table',
18263                     cls: 'table-condensed',
18264                     cn:[
18265                         Roo.bootstrap.DateField.content
18266                     ]
18267                 }
18268                 ]
18269             }
18270         ]
18271     }
18272 });
18273
18274  
18275
18276  
18277  /*
18278  * - LGPL
18279  *
18280  * CheckBox
18281  * 
18282  */
18283
18284 /**
18285  * @class Roo.bootstrap.CheckBox
18286  * @extends Roo.bootstrap.Input
18287  * Bootstrap CheckBox class
18288  * 
18289  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18290  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18291  * @cfg {String} boxLabel The text that appears beside the checkbox
18292  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18293  * @cfg {Boolean} checked initnal the element
18294  * @cfg {Boolean} inline inline the element (default false)
18295  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18296  * 
18297  * @constructor
18298  * Create a new CheckBox
18299  * @param {Object} config The config object
18300  */
18301
18302 Roo.bootstrap.CheckBox = function(config){
18303     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18304    
18305     this.addEvents({
18306         /**
18307         * @event check
18308         * Fires when the element is checked or unchecked.
18309         * @param {Roo.bootstrap.CheckBox} this This input
18310         * @param {Boolean} checked The new checked value
18311         */
18312        check : true
18313     });
18314     
18315 };
18316
18317 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18318   
18319     inputType: 'checkbox',
18320     inputValue: 1,
18321     valueOff: 0,
18322     boxLabel: false,
18323     checked: false,
18324     weight : false,
18325     inline: false,
18326     
18327     getAutoCreate : function()
18328     {
18329         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18330         
18331         var id = Roo.id();
18332         
18333         var cfg = {};
18334         
18335         cfg.cls = 'form-group ' + this.inputType; //input-group
18336         
18337         if(this.inline){
18338             cfg.cls += ' ' + this.inputType + '-inline';
18339         }
18340         
18341         var input =  {
18342             tag: 'input',
18343             id : id,
18344             type : this.inputType,
18345             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18346             cls : 'roo-' + this.inputType, //'form-box',
18347             placeholder : this.placeholder || ''
18348             
18349         };
18350         
18351         if (this.weight) { // Validity check?
18352             cfg.cls += " " + this.inputType + "-" + this.weight;
18353         }
18354         
18355         if (this.disabled) {
18356             input.disabled=true;
18357         }
18358         
18359         if(this.checked){
18360             input.checked = this.checked;
18361         }
18362         
18363         if (this.name) {
18364             input.name = this.name;
18365         }
18366         
18367         if (this.size) {
18368             input.cls += ' input-' + this.size;
18369         }
18370         
18371         var settings=this;
18372         
18373         ['xs','sm','md','lg'].map(function(size){
18374             if (settings[size]) {
18375                 cfg.cls += ' col-' + size + '-' + settings[size];
18376             }
18377         });
18378         
18379         var inputblock = input;
18380          
18381         if (this.before || this.after) {
18382             
18383             inputblock = {
18384                 cls : 'input-group',
18385                 cn :  [] 
18386             };
18387             
18388             if (this.before) {
18389                 inputblock.cn.push({
18390                     tag :'span',
18391                     cls : 'input-group-addon',
18392                     html : this.before
18393                 });
18394             }
18395             
18396             inputblock.cn.push(input);
18397             
18398             if (this.after) {
18399                 inputblock.cn.push({
18400                     tag :'span',
18401                     cls : 'input-group-addon',
18402                     html : this.after
18403                 });
18404             }
18405             
18406         }
18407         
18408         if (align ==='left' && this.fieldLabel.length) {
18409                 Roo.log("left and has label");
18410                 cfg.cn = [
18411                     
18412                     {
18413                         tag: 'label',
18414                         'for' :  id,
18415                         cls : 'control-label col-md-' + this.labelWidth,
18416                         html : this.fieldLabel
18417                         
18418                     },
18419                     {
18420                         cls : "col-md-" + (12 - this.labelWidth), 
18421                         cn: [
18422                             inputblock
18423                         ]
18424                     }
18425                     
18426                 ];
18427         } else if ( this.fieldLabel.length) {
18428                 Roo.log(" label");
18429                 cfg.cn = [
18430                    
18431                     {
18432                         tag: this.boxLabel ? 'span' : 'label',
18433                         'for': id,
18434                         cls: 'control-label box-input-label',
18435                         //cls : 'input-group-addon',
18436                         html : this.fieldLabel
18437                         
18438                     },
18439                     
18440                     inputblock
18441                     
18442                 ];
18443
18444         } else {
18445             
18446                 Roo.log(" no label && no align");
18447                 cfg.cn = [  inputblock ] ;
18448                 
18449                 
18450         }
18451         if(this.boxLabel){
18452              var boxLabelCfg = {
18453                 tag: 'label',
18454                 //'for': id, // box label is handled by onclick - so no for...
18455                 cls: 'box-label',
18456                 html: this.boxLabel
18457             };
18458             
18459             if(this.tooltip){
18460                 boxLabelCfg.tooltip = this.tooltip;
18461             }
18462              
18463             cfg.cn.push(boxLabelCfg);
18464         }
18465         
18466         
18467        
18468         return cfg;
18469         
18470     },
18471     
18472     /**
18473      * return the real input element.
18474      */
18475     inputEl: function ()
18476     {
18477         return this.el.select('input.roo-' + this.inputType,true).first();
18478     },
18479     
18480     labelEl: function()
18481     {
18482         return this.el.select('label.control-label',true).first();
18483     },
18484     /* depricated... */
18485     
18486     label: function()
18487     {
18488         return this.labelEl();
18489     },
18490     
18491     initEvents : function()
18492     {
18493 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18494         
18495         this.inputEl().on('click', this.onClick,  this);
18496         
18497         if (this.boxLabel) { 
18498             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18499         }
18500         
18501         this.startValue = this.getValue();
18502         
18503         if(this.groupId){
18504             Roo.bootstrap.CheckBox.register(this);
18505         }
18506     },
18507     
18508     onClick : function()
18509     {   
18510         this.setChecked(!this.checked);
18511     },
18512     
18513     setChecked : function(state,suppressEvent)
18514     {
18515         this.startValue = this.getValue();
18516         
18517         if(this.inputType == 'radio'){
18518             
18519             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18520                 e.dom.checked = false;
18521             });
18522             
18523             this.inputEl().dom.checked = true;
18524             
18525             this.inputEl().dom.value = this.inputValue;
18526             
18527             if(suppressEvent !== true){
18528                 this.fireEvent('check', this, true);
18529             }
18530             
18531             this.validate();
18532             
18533             return;
18534         }
18535         
18536         this.checked = state;
18537         
18538         this.inputEl().dom.checked = state;
18539         
18540         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18541         
18542         if(suppressEvent !== true){
18543             this.fireEvent('check', this, state);
18544         }
18545         
18546         this.validate();
18547     },
18548     
18549     getValue : function()
18550     {
18551         if(this.inputType == 'radio'){
18552             return this.getGroupValue();
18553         }
18554         
18555         return this.inputEl().getValue();
18556         
18557     },
18558     
18559     getGroupValue : function()
18560     {
18561         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18562             return '';
18563         }
18564         
18565         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18566     },
18567     
18568     setValue : function(v,suppressEvent)
18569     {
18570         if(this.inputType == 'radio'){
18571             this.setGroupValue(v, suppressEvent);
18572             return;
18573         }
18574         
18575         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18576         
18577         this.validate();
18578     },
18579     
18580     setGroupValue : function(v, suppressEvent)
18581     {
18582         this.startValue = this.getValue();
18583         
18584         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18585             e.dom.checked = false;
18586             
18587             if(e.dom.value == v){
18588                 e.dom.checked = true;
18589             }
18590         });
18591         
18592         if(suppressEvent !== true){
18593             this.fireEvent('check', this, true);
18594         }
18595
18596         this.validate();
18597         
18598         return;
18599     },
18600     
18601     validate : function()
18602     {
18603         if(
18604                 this.disabled || 
18605                 (this.inputType == 'radio' && this.validateRadio()) ||
18606                 (this.inputType == 'checkbox' && this.validateCheckbox())
18607         ){
18608             this.markValid();
18609             return true;
18610         }
18611         
18612         this.markInvalid();
18613         return false;
18614     },
18615     
18616     validateRadio : function()
18617     {
18618         var valid = false;
18619         
18620         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18621             if(!e.dom.checked){
18622                 return;
18623             }
18624             
18625             valid = true;
18626             
18627             return false;
18628         });
18629         
18630         return valid;
18631     },
18632     
18633     validateCheckbox : function()
18634     {
18635         if(!this.groupId){
18636             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18637         }
18638         
18639         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18640         
18641         if(!group){
18642             return false;
18643         }
18644         
18645         var r = false;
18646         
18647         for(var i in group){
18648             if(r){
18649                 break;
18650             }
18651             
18652             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18653         }
18654         
18655         return r;
18656     },
18657     
18658     /**
18659      * Mark this field as valid
18660      */
18661     markValid : function()
18662     {
18663         if(this.allowBlank){
18664             return;
18665         }
18666         
18667         var _this = this;
18668         
18669         this.fireEvent('valid', this);
18670         
18671         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18672         
18673         if(this.groupId){
18674             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18675         }
18676         
18677         if(label){
18678             label.markValid();
18679         }
18680         
18681         if(this.inputType == 'radio'){
18682             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18683                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18684                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18685             });
18686             
18687             return;
18688         }
18689         
18690         if(!this.groupId){
18691             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18692             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18693             return;
18694         }
18695         
18696         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18697             
18698         if(!group){
18699             return;
18700         }
18701         
18702         for(var i in group){
18703             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18704             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18705         }
18706     },
18707     
18708      /**
18709      * Mark this field as invalid
18710      * @param {String} msg The validation message
18711      */
18712     markInvalid : function(msg)
18713     {
18714         if(this.allowBlank){
18715             return;
18716         }
18717         
18718         var _this = this;
18719         
18720         this.fireEvent('invalid', this, msg);
18721         
18722         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18723         
18724         if(this.groupId){
18725             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18726         }
18727         
18728         if(label){
18729             label.markInvalid();
18730         }
18731             
18732         if(this.inputType == 'radio'){
18733             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18734                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18735                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18736             });
18737             
18738             return;
18739         }
18740         
18741         if(!this.groupId){
18742             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18743             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18744             return;
18745         }
18746         
18747         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18748         
18749         if(!group){
18750             return;
18751         }
18752         
18753         for(var i in group){
18754             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18755             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18756         }
18757         
18758     }
18759     
18760 });
18761
18762 Roo.apply(Roo.bootstrap.CheckBox, {
18763     
18764     groups: {},
18765     
18766      /**
18767     * register a CheckBox Group
18768     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18769     */
18770     register : function(checkbox)
18771     {
18772         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18773             this.groups[checkbox.groupId] = {};
18774         }
18775         
18776         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18777             return;
18778         }
18779         
18780         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18781         
18782     },
18783     /**
18784     * fetch a CheckBox Group based on the group ID
18785     * @param {string} the group ID
18786     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18787     */
18788     get: function(groupId) {
18789         if (typeof(this.groups[groupId]) == 'undefined') {
18790             return false;
18791         }
18792         
18793         return this.groups[groupId] ;
18794     }
18795     
18796     
18797 });
18798 /*
18799  * - LGPL
18800  *
18801  * Radio
18802  *
18803  *
18804  * not inline
18805  *<div class="radio">
18806   <label>
18807     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18808     Option one is this and that&mdash;be sure to include why it's great
18809   </label>
18810 </div>
18811  *
18812  *
18813  *inline
18814  *<span>
18815  *<label class="radio-inline">fieldLabel</label>
18816  *<label class="radio-inline">
18817   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18818 </label>
18819 <span>
18820  * 
18821  * 
18822  */
18823
18824 /**
18825  * @class Roo.bootstrap.Radio
18826  * @extends Roo.bootstrap.CheckBox
18827  * Bootstrap Radio class
18828
18829  * @constructor
18830  * Create a new Radio
18831  * @param {Object} config The config object
18832  */
18833
18834 Roo.bootstrap.Radio = function(config){
18835     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18836    
18837 };
18838
18839 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18840     
18841     inputType: 'radio',
18842     inputValue: '',
18843     valueOff: '',
18844     
18845     getAutoCreate : function()
18846     {
18847         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18848         align = align || 'left'; // default...
18849         
18850         
18851         
18852         var id = Roo.id();
18853         
18854         var cfg = {
18855                 tag : this.inline ? 'span' : 'div',
18856                 cls : '',
18857                 cn : []
18858         };
18859         
18860         var inline = this.inline ? ' radio-inline' : '';
18861         
18862         var lbl = {
18863                 tag: 'label' ,
18864                 // does not need for, as we wrap the input with it..
18865                 'for' : id,
18866                 cls : 'control-label box-label' + inline,
18867                 cn : []
18868         };
18869         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18870         
18871         var fieldLabel = {
18872             tag: 'label' ,
18873             //cls : 'control-label' + inline,
18874             html : this.fieldLabel,
18875             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18876         };
18877         
18878  
18879         
18880         
18881         var input =  {
18882             tag: 'input',
18883             id : id,
18884             type : this.inputType,
18885             //value : (!this.checked) ? this.valueOff : this.inputValue,
18886             value : this.inputValue,
18887             cls : 'roo-radio',
18888             placeholder : this.placeholder || '' // ?? needed????
18889             
18890         };
18891         if (this.weight) { // Validity check?
18892             input.cls += " radio-" + this.weight;
18893         }
18894         if (this.disabled) {
18895             input.disabled=true;
18896         }
18897         
18898         if(this.checked){
18899             input.checked = this.checked;
18900         }
18901         
18902         if (this.name) {
18903             input.name = this.name;
18904         }
18905         
18906         if (this.size) {
18907             input.cls += ' input-' + this.size;
18908         }
18909         
18910         //?? can span's inline have a width??
18911         
18912         var settings=this;
18913         ['xs','sm','md','lg'].map(function(size){
18914             if (settings[size]) {
18915                 cfg.cls += ' col-' + size + '-' + settings[size];
18916             }
18917         });
18918         
18919         var inputblock = input;
18920         
18921         if (this.before || this.after) {
18922             
18923             inputblock = {
18924                 cls : 'input-group',
18925                 tag : 'span',
18926                 cn :  [] 
18927             };
18928             if (this.before) {
18929                 inputblock.cn.push({
18930                     tag :'span',
18931                     cls : 'input-group-addon',
18932                     html : this.before
18933                 });
18934             }
18935             inputblock.cn.push(input);
18936             if (this.after) {
18937                 inputblock.cn.push({
18938                     tag :'span',
18939                     cls : 'input-group-addon',
18940                     html : this.after
18941                 });
18942             }
18943             
18944         };
18945         
18946         
18947         if (this.fieldLabel && this.fieldLabel.length) {
18948             cfg.cn.push(fieldLabel);
18949         }
18950        
18951         // normal bootstrap puts the input inside the label.
18952         // however with our styled version - it has to go after the input.
18953        
18954         //lbl.cn.push(inputblock);
18955         
18956         var lblwrap =  {
18957             tag: 'span',
18958             cls: 'radio' + inline,
18959             cn: [
18960                 inputblock,
18961                 lbl
18962             ]
18963         };
18964         
18965         cfg.cn.push( lblwrap);
18966         
18967         if(this.boxLabel){
18968             lbl.cn.push({
18969                 tag: 'span',
18970                 html: this.boxLabel
18971             })
18972         }
18973          
18974         
18975         return cfg;
18976         
18977     },
18978     
18979     initEvents : function()
18980     {
18981 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18982         
18983         this.inputEl().on('click', this.onClick,  this);
18984         if (this.boxLabel) {
18985             Roo.log('find label')
18986             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18987         }
18988         
18989     },
18990     
18991     inputEl: function ()
18992     {
18993         return this.el.select('input.roo-radio',true).first();
18994     },
18995     onClick : function()
18996     {   
18997         Roo.log("click");
18998         this.setChecked(true);
18999     },
19000     
19001     setChecked : function(state,suppressEvent)
19002     {
19003         if(state){
19004             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19005                 v.dom.checked = false;
19006             });
19007         }
19008         Roo.log(this.inputEl().dom);
19009         this.checked = state;
19010         this.inputEl().dom.checked = state;
19011         
19012         if(suppressEvent !== true){
19013             this.fireEvent('check', this, state);
19014         }
19015         
19016         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19017         
19018     },
19019     
19020     getGroupValue : function()
19021     {
19022         var value = '';
19023         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19024             if(v.dom.checked == true){
19025                 value = v.dom.value;
19026             }
19027         });
19028         
19029         return value;
19030     },
19031     
19032     /**
19033      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19034      * @return {Mixed} value The field value
19035      */
19036     getValue : function(){
19037         return this.getGroupValue();
19038     }
19039     
19040 });
19041
19042  
19043 //<script type="text/javascript">
19044
19045 /*
19046  * Based  Ext JS Library 1.1.1
19047  * Copyright(c) 2006-2007, Ext JS, LLC.
19048  * LGPL
19049  *
19050  */
19051  
19052 /**
19053  * @class Roo.HtmlEditorCore
19054  * @extends Roo.Component
19055  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19056  *
19057  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19058  */
19059
19060 Roo.HtmlEditorCore = function(config){
19061     
19062     
19063     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19064     
19065     
19066     this.addEvents({
19067         /**
19068          * @event initialize
19069          * Fires when the editor is fully initialized (including the iframe)
19070          * @param {Roo.HtmlEditorCore} this
19071          */
19072         initialize: true,
19073         /**
19074          * @event activate
19075          * Fires when the editor is first receives the focus. Any insertion must wait
19076          * until after this event.
19077          * @param {Roo.HtmlEditorCore} this
19078          */
19079         activate: true,
19080          /**
19081          * @event beforesync
19082          * Fires before the textarea is updated with content from the editor iframe. Return false
19083          * to cancel the sync.
19084          * @param {Roo.HtmlEditorCore} this
19085          * @param {String} html
19086          */
19087         beforesync: true,
19088          /**
19089          * @event beforepush
19090          * Fires before the iframe editor is updated with content from the textarea. Return false
19091          * to cancel the push.
19092          * @param {Roo.HtmlEditorCore} this
19093          * @param {String} html
19094          */
19095         beforepush: true,
19096          /**
19097          * @event sync
19098          * Fires when the textarea is updated with content from the editor iframe.
19099          * @param {Roo.HtmlEditorCore} this
19100          * @param {String} html
19101          */
19102         sync: true,
19103          /**
19104          * @event push
19105          * Fires when the iframe editor is updated with content from the textarea.
19106          * @param {Roo.HtmlEditorCore} this
19107          * @param {String} html
19108          */
19109         push: true,
19110         
19111         /**
19112          * @event editorevent
19113          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19114          * @param {Roo.HtmlEditorCore} this
19115          */
19116         editorevent: true
19117         
19118     });
19119     
19120     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19121     
19122     // defaults : white / black...
19123     this.applyBlacklists();
19124     
19125     
19126     
19127 };
19128
19129
19130 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19131
19132
19133      /**
19134      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19135      */
19136     
19137     owner : false,
19138     
19139      /**
19140      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19141      *                        Roo.resizable.
19142      */
19143     resizable : false,
19144      /**
19145      * @cfg {Number} height (in pixels)
19146      */   
19147     height: 300,
19148    /**
19149      * @cfg {Number} width (in pixels)
19150      */   
19151     width: 500,
19152     
19153     /**
19154      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19155      * 
19156      */
19157     stylesheets: false,
19158     
19159     // id of frame..
19160     frameId: false,
19161     
19162     // private properties
19163     validationEvent : false,
19164     deferHeight: true,
19165     initialized : false,
19166     activated : false,
19167     sourceEditMode : false,
19168     onFocus : Roo.emptyFn,
19169     iframePad:3,
19170     hideMode:'offsets',
19171     
19172     clearUp: true,
19173     
19174     // blacklist + whitelisted elements..
19175     black: false,
19176     white: false,
19177      
19178     
19179
19180     /**
19181      * Protected method that will not generally be called directly. It
19182      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19183      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19184      */
19185     getDocMarkup : function(){
19186         // body styles..
19187         var st = '';
19188         
19189         // inherit styels from page...?? 
19190         if (this.stylesheets === false) {
19191             
19192             Roo.get(document.head).select('style').each(function(node) {
19193                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19194             });
19195             
19196             Roo.get(document.head).select('link').each(function(node) { 
19197                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19198             });
19199             
19200         } else if (!this.stylesheets.length) {
19201                 // simple..
19202                 st = '<style type="text/css">' +
19203                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19204                    '</style>';
19205         } else { 
19206             
19207         }
19208         
19209         st +=  '<style type="text/css">' +
19210             'IMG { cursor: pointer } ' +
19211         '</style>';
19212
19213         
19214         return '<html><head>' + st  +
19215             //<style type="text/css">' +
19216             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19217             //'</style>' +
19218             ' </head><body class="roo-htmleditor-body"></body></html>';
19219     },
19220
19221     // private
19222     onRender : function(ct, position)
19223     {
19224         var _t = this;
19225         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19226         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19227         
19228         
19229         this.el.dom.style.border = '0 none';
19230         this.el.dom.setAttribute('tabIndex', -1);
19231         this.el.addClass('x-hidden hide');
19232         
19233         
19234         
19235         if(Roo.isIE){ // fix IE 1px bogus margin
19236             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19237         }
19238        
19239         
19240         this.frameId = Roo.id();
19241         
19242          
19243         
19244         var iframe = this.owner.wrap.createChild({
19245             tag: 'iframe',
19246             cls: 'form-control', // bootstrap..
19247             id: this.frameId,
19248             name: this.frameId,
19249             frameBorder : 'no',
19250             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19251         }, this.el
19252         );
19253         
19254         
19255         this.iframe = iframe.dom;
19256
19257          this.assignDocWin();
19258         
19259         this.doc.designMode = 'on';
19260        
19261         this.doc.open();
19262         this.doc.write(this.getDocMarkup());
19263         this.doc.close();
19264
19265         
19266         var task = { // must defer to wait for browser to be ready
19267             run : function(){
19268                 //console.log("run task?" + this.doc.readyState);
19269                 this.assignDocWin();
19270                 if(this.doc.body || this.doc.readyState == 'complete'){
19271                     try {
19272                         this.doc.designMode="on";
19273                     } catch (e) {
19274                         return;
19275                     }
19276                     Roo.TaskMgr.stop(task);
19277                     this.initEditor.defer(10, this);
19278                 }
19279             },
19280             interval : 10,
19281             duration: 10000,
19282             scope: this
19283         };
19284         Roo.TaskMgr.start(task);
19285
19286     },
19287
19288     // private
19289     onResize : function(w, h)
19290     {
19291          Roo.log('resize: ' +w + ',' + h );
19292         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19293         if(!this.iframe){
19294             return;
19295         }
19296         if(typeof w == 'number'){
19297             
19298             this.iframe.style.width = w + 'px';
19299         }
19300         if(typeof h == 'number'){
19301             
19302             this.iframe.style.height = h + 'px';
19303             if(this.doc){
19304                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19305             }
19306         }
19307         
19308     },
19309
19310     /**
19311      * Toggles the editor between standard and source edit mode.
19312      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19313      */
19314     toggleSourceEdit : function(sourceEditMode){
19315         
19316         this.sourceEditMode = sourceEditMode === true;
19317         
19318         if(this.sourceEditMode){
19319  
19320             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19321             
19322         }else{
19323             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19324             //this.iframe.className = '';
19325             this.deferFocus();
19326         }
19327         //this.setSize(this.owner.wrap.getSize());
19328         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19329     },
19330
19331     
19332   
19333
19334     /**
19335      * Protected method that will not generally be called directly. If you need/want
19336      * custom HTML cleanup, this is the method you should override.
19337      * @param {String} html The HTML to be cleaned
19338      * return {String} The cleaned HTML
19339      */
19340     cleanHtml : function(html){
19341         html = String(html);
19342         if(html.length > 5){
19343             if(Roo.isSafari){ // strip safari nonsense
19344                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19345             }
19346         }
19347         if(html == '&nbsp;'){
19348             html = '';
19349         }
19350         return html;
19351     },
19352
19353     /**
19354      * HTML Editor -> Textarea
19355      * Protected method that will not generally be called directly. Syncs the contents
19356      * of the editor iframe with the textarea.
19357      */
19358     syncValue : function(){
19359         if(this.initialized){
19360             var bd = (this.doc.body || this.doc.documentElement);
19361             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19362             var html = bd.innerHTML;
19363             if(Roo.isSafari){
19364                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19365                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19366                 if(m && m[1]){
19367                     html = '<div style="'+m[0]+'">' + html + '</div>';
19368                 }
19369             }
19370             html = this.cleanHtml(html);
19371             // fix up the special chars.. normaly like back quotes in word...
19372             // however we do not want to do this with chinese..
19373             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19374                 var cc = b.charCodeAt();
19375                 if (
19376                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19377                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19378                     (cc >= 0xf900 && cc < 0xfb00 )
19379                 ) {
19380                         return b;
19381                 }
19382                 return "&#"+cc+";" 
19383             });
19384             if(this.owner.fireEvent('beforesync', this, html) !== false){
19385                 this.el.dom.value = html;
19386                 this.owner.fireEvent('sync', this, html);
19387             }
19388         }
19389     },
19390
19391     /**
19392      * Protected method that will not generally be called directly. Pushes the value of the textarea
19393      * into the iframe editor.
19394      */
19395     pushValue : function(){
19396         if(this.initialized){
19397             var v = this.el.dom.value.trim();
19398             
19399 //            if(v.length < 1){
19400 //                v = '&#160;';
19401 //            }
19402             
19403             if(this.owner.fireEvent('beforepush', this, v) !== false){
19404                 var d = (this.doc.body || this.doc.documentElement);
19405                 d.innerHTML = v;
19406                 this.cleanUpPaste();
19407                 this.el.dom.value = d.innerHTML;
19408                 this.owner.fireEvent('push', this, v);
19409             }
19410         }
19411     },
19412
19413     // private
19414     deferFocus : function(){
19415         this.focus.defer(10, this);
19416     },
19417
19418     // doc'ed in Field
19419     focus : function(){
19420         if(this.win && !this.sourceEditMode){
19421             this.win.focus();
19422         }else{
19423             this.el.focus();
19424         }
19425     },
19426     
19427     assignDocWin: function()
19428     {
19429         var iframe = this.iframe;
19430         
19431          if(Roo.isIE){
19432             this.doc = iframe.contentWindow.document;
19433             this.win = iframe.contentWindow;
19434         } else {
19435 //            if (!Roo.get(this.frameId)) {
19436 //                return;
19437 //            }
19438 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19439 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19440             
19441             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19442                 return;
19443             }
19444             
19445             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19446             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19447         }
19448     },
19449     
19450     // private
19451     initEditor : function(){
19452         //console.log("INIT EDITOR");
19453         this.assignDocWin();
19454         
19455         
19456         
19457         this.doc.designMode="on";
19458         this.doc.open();
19459         this.doc.write(this.getDocMarkup());
19460         this.doc.close();
19461         
19462         var dbody = (this.doc.body || this.doc.documentElement);
19463         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19464         // this copies styles from the containing element into thsi one..
19465         // not sure why we need all of this..
19466         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19467         
19468         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19469         //ss['background-attachment'] = 'fixed'; // w3c
19470         dbody.bgProperties = 'fixed'; // ie
19471         //Roo.DomHelper.applyStyles(dbody, ss);
19472         Roo.EventManager.on(this.doc, {
19473             //'mousedown': this.onEditorEvent,
19474             'mouseup': this.onEditorEvent,
19475             'dblclick': this.onEditorEvent,
19476             'click': this.onEditorEvent,
19477             'keyup': this.onEditorEvent,
19478             buffer:100,
19479             scope: this
19480         });
19481         if(Roo.isGecko){
19482             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19483         }
19484         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19485             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19486         }
19487         this.initialized = true;
19488
19489         this.owner.fireEvent('initialize', this);
19490         this.pushValue();
19491     },
19492
19493     // private
19494     onDestroy : function(){
19495         
19496         
19497         
19498         if(this.rendered){
19499             
19500             //for (var i =0; i < this.toolbars.length;i++) {
19501             //    // fixme - ask toolbars for heights?
19502             //    this.toolbars[i].onDestroy();
19503            // }
19504             
19505             //this.wrap.dom.innerHTML = '';
19506             //this.wrap.remove();
19507         }
19508     },
19509
19510     // private
19511     onFirstFocus : function(){
19512         
19513         this.assignDocWin();
19514         
19515         
19516         this.activated = true;
19517          
19518     
19519         if(Roo.isGecko){ // prevent silly gecko errors
19520             this.win.focus();
19521             var s = this.win.getSelection();
19522             if(!s.focusNode || s.focusNode.nodeType != 3){
19523                 var r = s.getRangeAt(0);
19524                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19525                 r.collapse(true);
19526                 this.deferFocus();
19527             }
19528             try{
19529                 this.execCmd('useCSS', true);
19530                 this.execCmd('styleWithCSS', false);
19531             }catch(e){}
19532         }
19533         this.owner.fireEvent('activate', this);
19534     },
19535
19536     // private
19537     adjustFont: function(btn){
19538         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19539         //if(Roo.isSafari){ // safari
19540         //    adjust *= 2;
19541        // }
19542         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19543         if(Roo.isSafari){ // safari
19544             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19545             v =  (v < 10) ? 10 : v;
19546             v =  (v > 48) ? 48 : v;
19547             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19548             
19549         }
19550         
19551         
19552         v = Math.max(1, v+adjust);
19553         
19554         this.execCmd('FontSize', v  );
19555     },
19556
19557     onEditorEvent : function(e)
19558     {
19559         this.owner.fireEvent('editorevent', this, e);
19560       //  this.updateToolbar();
19561         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19562     },
19563
19564     insertTag : function(tg)
19565     {
19566         // could be a bit smarter... -> wrap the current selected tRoo..
19567         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19568             
19569             range = this.createRange(this.getSelection());
19570             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19571             wrappingNode.appendChild(range.extractContents());
19572             range.insertNode(wrappingNode);
19573
19574             return;
19575             
19576             
19577             
19578         }
19579         this.execCmd("formatblock",   tg);
19580         
19581     },
19582     
19583     insertText : function(txt)
19584     {
19585         
19586         
19587         var range = this.createRange();
19588         range.deleteContents();
19589                //alert(Sender.getAttribute('label'));
19590                
19591         range.insertNode(this.doc.createTextNode(txt));
19592     } ,
19593     
19594      
19595
19596     /**
19597      * Executes a Midas editor command on the editor document and performs necessary focus and
19598      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19599      * @param {String} cmd The Midas command
19600      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19601      */
19602     relayCmd : function(cmd, value){
19603         this.win.focus();
19604         this.execCmd(cmd, value);
19605         this.owner.fireEvent('editorevent', this);
19606         //this.updateToolbar();
19607         this.owner.deferFocus();
19608     },
19609
19610     /**
19611      * Executes a Midas editor command directly on the editor document.
19612      * For visual commands, you should use {@link #relayCmd} instead.
19613      * <b>This should only be called after the editor is initialized.</b>
19614      * @param {String} cmd The Midas command
19615      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19616      */
19617     execCmd : function(cmd, value){
19618         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19619         this.syncValue();
19620     },
19621  
19622  
19623    
19624     /**
19625      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19626      * to insert tRoo.
19627      * @param {String} text | dom node.. 
19628      */
19629     insertAtCursor : function(text)
19630     {
19631         
19632         
19633         
19634         if(!this.activated){
19635             return;
19636         }
19637         /*
19638         if(Roo.isIE){
19639             this.win.focus();
19640             var r = this.doc.selection.createRange();
19641             if(r){
19642                 r.collapse(true);
19643                 r.pasteHTML(text);
19644                 this.syncValue();
19645                 this.deferFocus();
19646             
19647             }
19648             return;
19649         }
19650         */
19651         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19652             this.win.focus();
19653             
19654             
19655             // from jquery ui (MIT licenced)
19656             var range, node;
19657             var win = this.win;
19658             
19659             if (win.getSelection && win.getSelection().getRangeAt) {
19660                 range = win.getSelection().getRangeAt(0);
19661                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19662                 range.insertNode(node);
19663             } else if (win.document.selection && win.document.selection.createRange) {
19664                 // no firefox support
19665                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19666                 win.document.selection.createRange().pasteHTML(txt);
19667             } else {
19668                 // no firefox support
19669                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19670                 this.execCmd('InsertHTML', txt);
19671             } 
19672             
19673             this.syncValue();
19674             
19675             this.deferFocus();
19676         }
19677     },
19678  // private
19679     mozKeyPress : function(e){
19680         if(e.ctrlKey){
19681             var c = e.getCharCode(), cmd;
19682           
19683             if(c > 0){
19684                 c = String.fromCharCode(c).toLowerCase();
19685                 switch(c){
19686                     case 'b':
19687                         cmd = 'bold';
19688                         break;
19689                     case 'i':
19690                         cmd = 'italic';
19691                         break;
19692                     
19693                     case 'u':
19694                         cmd = 'underline';
19695                         break;
19696                     
19697                     case 'v':
19698                         this.cleanUpPaste.defer(100, this);
19699                         return;
19700                         
19701                 }
19702                 if(cmd){
19703                     this.win.focus();
19704                     this.execCmd(cmd);
19705                     this.deferFocus();
19706                     e.preventDefault();
19707                 }
19708                 
19709             }
19710         }
19711     },
19712
19713     // private
19714     fixKeys : function(){ // load time branching for fastest keydown performance
19715         if(Roo.isIE){
19716             return function(e){
19717                 var k = e.getKey(), r;
19718                 if(k == e.TAB){
19719                     e.stopEvent();
19720                     r = this.doc.selection.createRange();
19721                     if(r){
19722                         r.collapse(true);
19723                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19724                         this.deferFocus();
19725                     }
19726                     return;
19727                 }
19728                 
19729                 if(k == e.ENTER){
19730                     r = this.doc.selection.createRange();
19731                     if(r){
19732                         var target = r.parentElement();
19733                         if(!target || target.tagName.toLowerCase() != 'li'){
19734                             e.stopEvent();
19735                             r.pasteHTML('<br />');
19736                             r.collapse(false);
19737                             r.select();
19738                         }
19739                     }
19740                 }
19741                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19742                     this.cleanUpPaste.defer(100, this);
19743                     return;
19744                 }
19745                 
19746                 
19747             };
19748         }else if(Roo.isOpera){
19749             return function(e){
19750                 var k = e.getKey();
19751                 if(k == e.TAB){
19752                     e.stopEvent();
19753                     this.win.focus();
19754                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19755                     this.deferFocus();
19756                 }
19757                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19758                     this.cleanUpPaste.defer(100, this);
19759                     return;
19760                 }
19761                 
19762             };
19763         }else if(Roo.isSafari){
19764             return function(e){
19765                 var k = e.getKey();
19766                 
19767                 if(k == e.TAB){
19768                     e.stopEvent();
19769                     this.execCmd('InsertText','\t');
19770                     this.deferFocus();
19771                     return;
19772                 }
19773                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19774                     this.cleanUpPaste.defer(100, this);
19775                     return;
19776                 }
19777                 
19778              };
19779         }
19780     }(),
19781     
19782     getAllAncestors: function()
19783     {
19784         var p = this.getSelectedNode();
19785         var a = [];
19786         if (!p) {
19787             a.push(p); // push blank onto stack..
19788             p = this.getParentElement();
19789         }
19790         
19791         
19792         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19793             a.push(p);
19794             p = p.parentNode;
19795         }
19796         a.push(this.doc.body);
19797         return a;
19798     },
19799     lastSel : false,
19800     lastSelNode : false,
19801     
19802     
19803     getSelection : function() 
19804     {
19805         this.assignDocWin();
19806         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19807     },
19808     
19809     getSelectedNode: function() 
19810     {
19811         // this may only work on Gecko!!!
19812         
19813         // should we cache this!!!!
19814         
19815         
19816         
19817          
19818         var range = this.createRange(this.getSelection()).cloneRange();
19819         
19820         if (Roo.isIE) {
19821             var parent = range.parentElement();
19822             while (true) {
19823                 var testRange = range.duplicate();
19824                 testRange.moveToElementText(parent);
19825                 if (testRange.inRange(range)) {
19826                     break;
19827                 }
19828                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19829                     break;
19830                 }
19831                 parent = parent.parentElement;
19832             }
19833             return parent;
19834         }
19835         
19836         // is ancestor a text element.
19837         var ac =  range.commonAncestorContainer;
19838         if (ac.nodeType == 3) {
19839             ac = ac.parentNode;
19840         }
19841         
19842         var ar = ac.childNodes;
19843          
19844         var nodes = [];
19845         var other_nodes = [];
19846         var has_other_nodes = false;
19847         for (var i=0;i<ar.length;i++) {
19848             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19849                 continue;
19850             }
19851             // fullly contained node.
19852             
19853             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19854                 nodes.push(ar[i]);
19855                 continue;
19856             }
19857             
19858             // probably selected..
19859             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19860                 other_nodes.push(ar[i]);
19861                 continue;
19862             }
19863             // outer..
19864             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19865                 continue;
19866             }
19867             
19868             
19869             has_other_nodes = true;
19870         }
19871         if (!nodes.length && other_nodes.length) {
19872             nodes= other_nodes;
19873         }
19874         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19875             return false;
19876         }
19877         
19878         return nodes[0];
19879     },
19880     createRange: function(sel)
19881     {
19882         // this has strange effects when using with 
19883         // top toolbar - not sure if it's a great idea.
19884         //this.editor.contentWindow.focus();
19885         if (typeof sel != "undefined") {
19886             try {
19887                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19888             } catch(e) {
19889                 return this.doc.createRange();
19890             }
19891         } else {
19892             return this.doc.createRange();
19893         }
19894     },
19895     getParentElement: function()
19896     {
19897         
19898         this.assignDocWin();
19899         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19900         
19901         var range = this.createRange(sel);
19902          
19903         try {
19904             var p = range.commonAncestorContainer;
19905             while (p.nodeType == 3) { // text node
19906                 p = p.parentNode;
19907             }
19908             return p;
19909         } catch (e) {
19910             return null;
19911         }
19912     
19913     },
19914     /***
19915      *
19916      * Range intersection.. the hard stuff...
19917      *  '-1' = before
19918      *  '0' = hits..
19919      *  '1' = after.
19920      *         [ -- selected range --- ]
19921      *   [fail]                        [fail]
19922      *
19923      *    basically..
19924      *      if end is before start or  hits it. fail.
19925      *      if start is after end or hits it fail.
19926      *
19927      *   if either hits (but other is outside. - then it's not 
19928      *   
19929      *    
19930      **/
19931     
19932     
19933     // @see http://www.thismuchiknow.co.uk/?p=64.
19934     rangeIntersectsNode : function(range, node)
19935     {
19936         var nodeRange = node.ownerDocument.createRange();
19937         try {
19938             nodeRange.selectNode(node);
19939         } catch (e) {
19940             nodeRange.selectNodeContents(node);
19941         }
19942     
19943         var rangeStartRange = range.cloneRange();
19944         rangeStartRange.collapse(true);
19945     
19946         var rangeEndRange = range.cloneRange();
19947         rangeEndRange.collapse(false);
19948     
19949         var nodeStartRange = nodeRange.cloneRange();
19950         nodeStartRange.collapse(true);
19951     
19952         var nodeEndRange = nodeRange.cloneRange();
19953         nodeEndRange.collapse(false);
19954     
19955         return rangeStartRange.compareBoundaryPoints(
19956                  Range.START_TO_START, nodeEndRange) == -1 &&
19957                rangeEndRange.compareBoundaryPoints(
19958                  Range.START_TO_START, nodeStartRange) == 1;
19959         
19960          
19961     },
19962     rangeCompareNode : function(range, node)
19963     {
19964         var nodeRange = node.ownerDocument.createRange();
19965         try {
19966             nodeRange.selectNode(node);
19967         } catch (e) {
19968             nodeRange.selectNodeContents(node);
19969         }
19970         
19971         
19972         range.collapse(true);
19973     
19974         nodeRange.collapse(true);
19975      
19976         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19977         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19978          
19979         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19980         
19981         var nodeIsBefore   =  ss == 1;
19982         var nodeIsAfter    = ee == -1;
19983         
19984         if (nodeIsBefore && nodeIsAfter)
19985             return 0; // outer
19986         if (!nodeIsBefore && nodeIsAfter)
19987             return 1; //right trailed.
19988         
19989         if (nodeIsBefore && !nodeIsAfter)
19990             return 2;  // left trailed.
19991         // fully contined.
19992         return 3;
19993     },
19994
19995     // private? - in a new class?
19996     cleanUpPaste :  function()
19997     {
19998         // cleans up the whole document..
19999         Roo.log('cleanuppaste');
20000         
20001         this.cleanUpChildren(this.doc.body);
20002         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20003         if (clean != this.doc.body.innerHTML) {
20004             this.doc.body.innerHTML = clean;
20005         }
20006         
20007     },
20008     
20009     cleanWordChars : function(input) {// change the chars to hex code
20010         var he = Roo.HtmlEditorCore;
20011         
20012         var output = input;
20013         Roo.each(he.swapCodes, function(sw) { 
20014             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20015             
20016             output = output.replace(swapper, sw[1]);
20017         });
20018         
20019         return output;
20020     },
20021     
20022     
20023     cleanUpChildren : function (n)
20024     {
20025         if (!n.childNodes.length) {
20026             return;
20027         }
20028         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20029            this.cleanUpChild(n.childNodes[i]);
20030         }
20031     },
20032     
20033     
20034         
20035     
20036     cleanUpChild : function (node)
20037     {
20038         var ed = this;
20039         //console.log(node);
20040         if (node.nodeName == "#text") {
20041             // clean up silly Windows -- stuff?
20042             return; 
20043         }
20044         if (node.nodeName == "#comment") {
20045             node.parentNode.removeChild(node);
20046             // clean up silly Windows -- stuff?
20047             return; 
20048         }
20049         var lcname = node.tagName.toLowerCase();
20050         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20051         // whitelist of tags..
20052         
20053         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20054             // remove node.
20055             node.parentNode.removeChild(node);
20056             return;
20057             
20058         }
20059         
20060         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20061         
20062         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20063         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20064         
20065         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20066         //    remove_keep_children = true;
20067         //}
20068         
20069         if (remove_keep_children) {
20070             this.cleanUpChildren(node);
20071             // inserts everything just before this node...
20072             while (node.childNodes.length) {
20073                 var cn = node.childNodes[0];
20074                 node.removeChild(cn);
20075                 node.parentNode.insertBefore(cn, node);
20076             }
20077             node.parentNode.removeChild(node);
20078             return;
20079         }
20080         
20081         if (!node.attributes || !node.attributes.length) {
20082             this.cleanUpChildren(node);
20083             return;
20084         }
20085         
20086         function cleanAttr(n,v)
20087         {
20088             
20089             if (v.match(/^\./) || v.match(/^\//)) {
20090                 return;
20091             }
20092             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20093                 return;
20094             }
20095             if (v.match(/^#/)) {
20096                 return;
20097             }
20098 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20099             node.removeAttribute(n);
20100             
20101         }
20102         
20103         var cwhite = this.cwhite;
20104         var cblack = this.cblack;
20105             
20106         function cleanStyle(n,v)
20107         {
20108             if (v.match(/expression/)) { //XSS?? should we even bother..
20109                 node.removeAttribute(n);
20110                 return;
20111             }
20112             
20113             var parts = v.split(/;/);
20114             var clean = [];
20115             
20116             Roo.each(parts, function(p) {
20117                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20118                 if (!p.length) {
20119                     return true;
20120                 }
20121                 var l = p.split(':').shift().replace(/\s+/g,'');
20122                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20123                 
20124                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20125 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20126                     //node.removeAttribute(n);
20127                     return true;
20128                 }
20129                 //Roo.log()
20130                 // only allow 'c whitelisted system attributes'
20131                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20132 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20133                     //node.removeAttribute(n);
20134                     return true;
20135                 }
20136                 
20137                 
20138                  
20139                 
20140                 clean.push(p);
20141                 return true;
20142             });
20143             if (clean.length) { 
20144                 node.setAttribute(n, clean.join(';'));
20145             } else {
20146                 node.removeAttribute(n);
20147             }
20148             
20149         }
20150         
20151         
20152         for (var i = node.attributes.length-1; i > -1 ; i--) {
20153             var a = node.attributes[i];
20154             //console.log(a);
20155             
20156             if (a.name.toLowerCase().substr(0,2)=='on')  {
20157                 node.removeAttribute(a.name);
20158                 continue;
20159             }
20160             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20161                 node.removeAttribute(a.name);
20162                 continue;
20163             }
20164             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20165                 cleanAttr(a.name,a.value); // fixme..
20166                 continue;
20167             }
20168             if (a.name == 'style') {
20169                 cleanStyle(a.name,a.value);
20170                 continue;
20171             }
20172             /// clean up MS crap..
20173             // tecnically this should be a list of valid class'es..
20174             
20175             
20176             if (a.name == 'class') {
20177                 if (a.value.match(/^Mso/)) {
20178                     node.className = '';
20179                 }
20180                 
20181                 if (a.value.match(/body/)) {
20182                     node.className = '';
20183                 }
20184                 continue;
20185             }
20186             
20187             // style cleanup!?
20188             // class cleanup?
20189             
20190         }
20191         
20192         
20193         this.cleanUpChildren(node);
20194         
20195         
20196     },
20197     
20198     /**
20199      * Clean up MS wordisms...
20200      */
20201     cleanWord : function(node)
20202     {
20203         
20204         
20205         if (!node) {
20206             this.cleanWord(this.doc.body);
20207             return;
20208         }
20209         if (node.nodeName == "#text") {
20210             // clean up silly Windows -- stuff?
20211             return; 
20212         }
20213         if (node.nodeName == "#comment") {
20214             node.parentNode.removeChild(node);
20215             // clean up silly Windows -- stuff?
20216             return; 
20217         }
20218         
20219         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20220             node.parentNode.removeChild(node);
20221             return;
20222         }
20223         
20224         // remove - but keep children..
20225         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20226             while (node.childNodes.length) {
20227                 var cn = node.childNodes[0];
20228                 node.removeChild(cn);
20229                 node.parentNode.insertBefore(cn, node);
20230             }
20231             node.parentNode.removeChild(node);
20232             this.iterateChildren(node, this.cleanWord);
20233             return;
20234         }
20235         // clean styles
20236         if (node.className.length) {
20237             
20238             var cn = node.className.split(/\W+/);
20239             var cna = [];
20240             Roo.each(cn, function(cls) {
20241                 if (cls.match(/Mso[a-zA-Z]+/)) {
20242                     return;
20243                 }
20244                 cna.push(cls);
20245             });
20246             node.className = cna.length ? cna.join(' ') : '';
20247             if (!cna.length) {
20248                 node.removeAttribute("class");
20249             }
20250         }
20251         
20252         if (node.hasAttribute("lang")) {
20253             node.removeAttribute("lang");
20254         }
20255         
20256         if (node.hasAttribute("style")) {
20257             
20258             var styles = node.getAttribute("style").split(";");
20259             var nstyle = [];
20260             Roo.each(styles, function(s) {
20261                 if (!s.match(/:/)) {
20262                     return;
20263                 }
20264                 var kv = s.split(":");
20265                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20266                     return;
20267                 }
20268                 // what ever is left... we allow.
20269                 nstyle.push(s);
20270             });
20271             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20272             if (!nstyle.length) {
20273                 node.removeAttribute('style');
20274             }
20275         }
20276         this.iterateChildren(node, this.cleanWord);
20277         
20278         
20279         
20280     },
20281     /**
20282      * iterateChildren of a Node, calling fn each time, using this as the scole..
20283      * @param {DomNode} node node to iterate children of.
20284      * @param {Function} fn method of this class to call on each item.
20285      */
20286     iterateChildren : function(node, fn)
20287     {
20288         if (!node.childNodes.length) {
20289                 return;
20290         }
20291         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20292            fn.call(this, node.childNodes[i])
20293         }
20294     },
20295     
20296     
20297     /**
20298      * cleanTableWidths.
20299      *
20300      * Quite often pasting from word etc.. results in tables with column and widths.
20301      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20302      *
20303      */
20304     cleanTableWidths : function(node)
20305     {
20306          
20307          
20308         if (!node) {
20309             this.cleanTableWidths(this.doc.body);
20310             return;
20311         }
20312         
20313         // ignore list...
20314         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20315             return; 
20316         }
20317         Roo.log(node.tagName);
20318         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20319             this.iterateChildren(node, this.cleanTableWidths);
20320             return;
20321         }
20322         if (node.hasAttribute('width')) {
20323             node.removeAttribute('width');
20324         }
20325         
20326          
20327         if (node.hasAttribute("style")) {
20328             // pretty basic...
20329             
20330             var styles = node.getAttribute("style").split(";");
20331             var nstyle = [];
20332             Roo.each(styles, function(s) {
20333                 if (!s.match(/:/)) {
20334                     return;
20335                 }
20336                 var kv = s.split(":");
20337                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20338                     return;
20339                 }
20340                 // what ever is left... we allow.
20341                 nstyle.push(s);
20342             });
20343             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20344             if (!nstyle.length) {
20345                 node.removeAttribute('style');
20346             }
20347         }
20348         
20349         this.iterateChildren(node, this.cleanTableWidths);
20350         
20351         
20352     },
20353     
20354     
20355     
20356     
20357     domToHTML : function(currentElement, depth, nopadtext) {
20358         
20359         depth = depth || 0;
20360         nopadtext = nopadtext || false;
20361     
20362         if (!currentElement) {
20363             return this.domToHTML(this.doc.body);
20364         }
20365         
20366         //Roo.log(currentElement);
20367         var j;
20368         var allText = false;
20369         var nodeName = currentElement.nodeName;
20370         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20371         
20372         if  (nodeName == '#text') {
20373             
20374             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20375         }
20376         
20377         
20378         var ret = '';
20379         if (nodeName != 'BODY') {
20380              
20381             var i = 0;
20382             // Prints the node tagName, such as <A>, <IMG>, etc
20383             if (tagName) {
20384                 var attr = [];
20385                 for(i = 0; i < currentElement.attributes.length;i++) {
20386                     // quoting?
20387                     var aname = currentElement.attributes.item(i).name;
20388                     if (!currentElement.attributes.item(i).value.length) {
20389                         continue;
20390                     }
20391                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20392                 }
20393                 
20394                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20395             } 
20396             else {
20397                 
20398                 // eack
20399             }
20400         } else {
20401             tagName = false;
20402         }
20403         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20404             return ret;
20405         }
20406         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20407             nopadtext = true;
20408         }
20409         
20410         
20411         // Traverse the tree
20412         i = 0;
20413         var currentElementChild = currentElement.childNodes.item(i);
20414         var allText = true;
20415         var innerHTML  = '';
20416         lastnode = '';
20417         while (currentElementChild) {
20418             // Formatting code (indent the tree so it looks nice on the screen)
20419             var nopad = nopadtext;
20420             if (lastnode == 'SPAN') {
20421                 nopad  = true;
20422             }
20423             // text
20424             if  (currentElementChild.nodeName == '#text') {
20425                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20426                 toadd = nopadtext ? toadd : toadd.trim();
20427                 if (!nopad && toadd.length > 80) {
20428                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20429                 }
20430                 innerHTML  += toadd;
20431                 
20432                 i++;
20433                 currentElementChild = currentElement.childNodes.item(i);
20434                 lastNode = '';
20435                 continue;
20436             }
20437             allText = false;
20438             
20439             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20440                 
20441             // Recursively traverse the tree structure of the child node
20442             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20443             lastnode = currentElementChild.nodeName;
20444             i++;
20445             currentElementChild=currentElement.childNodes.item(i);
20446         }
20447         
20448         ret += innerHTML;
20449         
20450         if (!allText) {
20451                 // The remaining code is mostly for formatting the tree
20452             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20453         }
20454         
20455         
20456         if (tagName) {
20457             ret+= "</"+tagName+">";
20458         }
20459         return ret;
20460         
20461     },
20462         
20463     applyBlacklists : function()
20464     {
20465         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20466         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20467         
20468         this.white = [];
20469         this.black = [];
20470         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20471             if (b.indexOf(tag) > -1) {
20472                 return;
20473             }
20474             this.white.push(tag);
20475             
20476         }, this);
20477         
20478         Roo.each(w, function(tag) {
20479             if (b.indexOf(tag) > -1) {
20480                 return;
20481             }
20482             if (this.white.indexOf(tag) > -1) {
20483                 return;
20484             }
20485             this.white.push(tag);
20486             
20487         }, this);
20488         
20489         
20490         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20491             if (w.indexOf(tag) > -1) {
20492                 return;
20493             }
20494             this.black.push(tag);
20495             
20496         }, this);
20497         
20498         Roo.each(b, function(tag) {
20499             if (w.indexOf(tag) > -1) {
20500                 return;
20501             }
20502             if (this.black.indexOf(tag) > -1) {
20503                 return;
20504             }
20505             this.black.push(tag);
20506             
20507         }, this);
20508         
20509         
20510         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20511         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20512         
20513         this.cwhite = [];
20514         this.cblack = [];
20515         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20516             if (b.indexOf(tag) > -1) {
20517                 return;
20518             }
20519             this.cwhite.push(tag);
20520             
20521         }, this);
20522         
20523         Roo.each(w, function(tag) {
20524             if (b.indexOf(tag) > -1) {
20525                 return;
20526             }
20527             if (this.cwhite.indexOf(tag) > -1) {
20528                 return;
20529             }
20530             this.cwhite.push(tag);
20531             
20532         }, this);
20533         
20534         
20535         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20536             if (w.indexOf(tag) > -1) {
20537                 return;
20538             }
20539             this.cblack.push(tag);
20540             
20541         }, this);
20542         
20543         Roo.each(b, function(tag) {
20544             if (w.indexOf(tag) > -1) {
20545                 return;
20546             }
20547             if (this.cblack.indexOf(tag) > -1) {
20548                 return;
20549             }
20550             this.cblack.push(tag);
20551             
20552         }, this);
20553     },
20554     
20555     setStylesheets : function(stylesheets)
20556     {
20557         if(typeof(stylesheets) == 'string'){
20558             Roo.get(this.iframe.contentDocument.head).createChild({
20559                 tag : 'link',
20560                 rel : 'stylesheet',
20561                 type : 'text/css',
20562                 href : stylesheets
20563             });
20564             
20565             return;
20566         }
20567         var _this = this;
20568      
20569         Roo.each(stylesheets, function(s) {
20570             if(!s.length){
20571                 return;
20572             }
20573             
20574             Roo.get(_this.iframe.contentDocument.head).createChild({
20575                 tag : 'link',
20576                 rel : 'stylesheet',
20577                 type : 'text/css',
20578                 href : s
20579             });
20580         });
20581
20582         
20583     },
20584     
20585     removeStylesheets : function()
20586     {
20587         var _this = this;
20588         
20589         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20590             s.remove();
20591         });
20592     }
20593     
20594     // hide stuff that is not compatible
20595     /**
20596      * @event blur
20597      * @hide
20598      */
20599     /**
20600      * @event change
20601      * @hide
20602      */
20603     /**
20604      * @event focus
20605      * @hide
20606      */
20607     /**
20608      * @event specialkey
20609      * @hide
20610      */
20611     /**
20612      * @cfg {String} fieldClass @hide
20613      */
20614     /**
20615      * @cfg {String} focusClass @hide
20616      */
20617     /**
20618      * @cfg {String} autoCreate @hide
20619      */
20620     /**
20621      * @cfg {String} inputType @hide
20622      */
20623     /**
20624      * @cfg {String} invalidClass @hide
20625      */
20626     /**
20627      * @cfg {String} invalidText @hide
20628      */
20629     /**
20630      * @cfg {String} msgFx @hide
20631      */
20632     /**
20633      * @cfg {String} validateOnBlur @hide
20634      */
20635 });
20636
20637 Roo.HtmlEditorCore.white = [
20638         'area', 'br', 'img', 'input', 'hr', 'wbr',
20639         
20640        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20641        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20642        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20643        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20644        'table',   'ul',         'xmp', 
20645        
20646        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20647       'thead',   'tr', 
20648      
20649       'dir', 'menu', 'ol', 'ul', 'dl',
20650        
20651       'embed',  'object'
20652 ];
20653
20654
20655 Roo.HtmlEditorCore.black = [
20656     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20657         'applet', // 
20658         'base',   'basefont', 'bgsound', 'blink',  'body', 
20659         'frame',  'frameset', 'head',    'html',   'ilayer', 
20660         'iframe', 'layer',  'link',     'meta',    'object',   
20661         'script', 'style' ,'title',  'xml' // clean later..
20662 ];
20663 Roo.HtmlEditorCore.clean = [
20664     'script', 'style', 'title', 'xml'
20665 ];
20666 Roo.HtmlEditorCore.remove = [
20667     'font'
20668 ];
20669 // attributes..
20670
20671 Roo.HtmlEditorCore.ablack = [
20672     'on'
20673 ];
20674     
20675 Roo.HtmlEditorCore.aclean = [ 
20676     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20677 ];
20678
20679 // protocols..
20680 Roo.HtmlEditorCore.pwhite= [
20681         'http',  'https',  'mailto'
20682 ];
20683
20684 // white listed style attributes.
20685 Roo.HtmlEditorCore.cwhite= [
20686       //  'text-align', /// default is to allow most things..
20687       
20688          
20689 //        'font-size'//??
20690 ];
20691
20692 // black listed style attributes.
20693 Roo.HtmlEditorCore.cblack= [
20694       //  'font-size' -- this can be set by the project 
20695 ];
20696
20697
20698 Roo.HtmlEditorCore.swapCodes   =[ 
20699     [    8211, "--" ], 
20700     [    8212, "--" ], 
20701     [    8216,  "'" ],  
20702     [    8217, "'" ],  
20703     [    8220, '"' ],  
20704     [    8221, '"' ],  
20705     [    8226, "*" ],  
20706     [    8230, "..." ]
20707 ]; 
20708
20709     /*
20710  * - LGPL
20711  *
20712  * HtmlEditor
20713  * 
20714  */
20715
20716 /**
20717  * @class Roo.bootstrap.HtmlEditor
20718  * @extends Roo.bootstrap.TextArea
20719  * Bootstrap HtmlEditor class
20720
20721  * @constructor
20722  * Create a new HtmlEditor
20723  * @param {Object} config The config object
20724  */
20725
20726 Roo.bootstrap.HtmlEditor = function(config){
20727     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20728     if (!this.toolbars) {
20729         this.toolbars = [];
20730     }
20731     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20732     this.addEvents({
20733             /**
20734              * @event initialize
20735              * Fires when the editor is fully initialized (including the iframe)
20736              * @param {HtmlEditor} this
20737              */
20738             initialize: true,
20739             /**
20740              * @event activate
20741              * Fires when the editor is first receives the focus. Any insertion must wait
20742              * until after this event.
20743              * @param {HtmlEditor} this
20744              */
20745             activate: true,
20746              /**
20747              * @event beforesync
20748              * Fires before the textarea is updated with content from the editor iframe. Return false
20749              * to cancel the sync.
20750              * @param {HtmlEditor} this
20751              * @param {String} html
20752              */
20753             beforesync: true,
20754              /**
20755              * @event beforepush
20756              * Fires before the iframe editor is updated with content from the textarea. Return false
20757              * to cancel the push.
20758              * @param {HtmlEditor} this
20759              * @param {String} html
20760              */
20761             beforepush: true,
20762              /**
20763              * @event sync
20764              * Fires when the textarea is updated with content from the editor iframe.
20765              * @param {HtmlEditor} this
20766              * @param {String} html
20767              */
20768             sync: true,
20769              /**
20770              * @event push
20771              * Fires when the iframe editor is updated with content from the textarea.
20772              * @param {HtmlEditor} this
20773              * @param {String} html
20774              */
20775             push: true,
20776              /**
20777              * @event editmodechange
20778              * Fires when the editor switches edit modes
20779              * @param {HtmlEditor} this
20780              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20781              */
20782             editmodechange: true,
20783             /**
20784              * @event editorevent
20785              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20786              * @param {HtmlEditor} this
20787              */
20788             editorevent: true,
20789             /**
20790              * @event firstfocus
20791              * Fires when on first focus - needed by toolbars..
20792              * @param {HtmlEditor} this
20793              */
20794             firstfocus: true,
20795             /**
20796              * @event autosave
20797              * Auto save the htmlEditor value as a file into Events
20798              * @param {HtmlEditor} this
20799              */
20800             autosave: true,
20801             /**
20802              * @event savedpreview
20803              * preview the saved version of htmlEditor
20804              * @param {HtmlEditor} this
20805              */
20806             savedpreview: true
20807         });
20808 };
20809
20810
20811 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20812     
20813     
20814       /**
20815      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20816      */
20817     toolbars : false,
20818    
20819      /**
20820      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20821      *                        Roo.resizable.
20822      */
20823     resizable : false,
20824      /**
20825      * @cfg {Number} height (in pixels)
20826      */   
20827     height: 300,
20828    /**
20829      * @cfg {Number} width (in pixels)
20830      */   
20831     width: false,
20832     
20833     /**
20834      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20835      * 
20836      */
20837     stylesheets: false,
20838     
20839     // id of frame..
20840     frameId: false,
20841     
20842     // private properties
20843     validationEvent : false,
20844     deferHeight: true,
20845     initialized : false,
20846     activated : false,
20847     
20848     onFocus : Roo.emptyFn,
20849     iframePad:3,
20850     hideMode:'offsets',
20851     
20852     
20853     tbContainer : false,
20854     
20855     toolbarContainer :function() {
20856         return this.wrap.select('.x-html-editor-tb',true).first();
20857     },
20858
20859     /**
20860      * Protected method that will not generally be called directly. It
20861      * is called when the editor creates its toolbar. Override this method if you need to
20862      * add custom toolbar buttons.
20863      * @param {HtmlEditor} editor
20864      */
20865     createToolbar : function(){
20866         
20867         Roo.log("create toolbars");
20868         
20869         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20870         this.toolbars[0].render(this.toolbarContainer());
20871         
20872         return;
20873         
20874 //        if (!editor.toolbars || !editor.toolbars.length) {
20875 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20876 //        }
20877 //        
20878 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20879 //            editor.toolbars[i] = Roo.factory(
20880 //                    typeof(editor.toolbars[i]) == 'string' ?
20881 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20882 //                Roo.bootstrap.HtmlEditor);
20883 //            editor.toolbars[i].init(editor);
20884 //        }
20885     },
20886
20887      
20888     // private
20889     onRender : function(ct, position)
20890     {
20891        // Roo.log("Call onRender: " + this.xtype);
20892         var _t = this;
20893         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20894       
20895         this.wrap = this.inputEl().wrap({
20896             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20897         });
20898         
20899         this.editorcore.onRender(ct, position);
20900          
20901         if (this.resizable) {
20902             this.resizeEl = new Roo.Resizable(this.wrap, {
20903                 pinned : true,
20904                 wrap: true,
20905                 dynamic : true,
20906                 minHeight : this.height,
20907                 height: this.height,
20908                 handles : this.resizable,
20909                 width: this.width,
20910                 listeners : {
20911                     resize : function(r, w, h) {
20912                         _t.onResize(w,h); // -something
20913                     }
20914                 }
20915             });
20916             
20917         }
20918         this.createToolbar(this);
20919        
20920         
20921         if(!this.width && this.resizable){
20922             this.setSize(this.wrap.getSize());
20923         }
20924         if (this.resizeEl) {
20925             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20926             // should trigger onReize..
20927         }
20928         
20929     },
20930
20931     // private
20932     onResize : function(w, h)
20933     {
20934         Roo.log('resize: ' +w + ',' + h );
20935         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20936         var ew = false;
20937         var eh = false;
20938         
20939         if(this.inputEl() ){
20940             if(typeof w == 'number'){
20941                 var aw = w - this.wrap.getFrameWidth('lr');
20942                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20943                 ew = aw;
20944             }
20945             if(typeof h == 'number'){
20946                  var tbh = -11;  // fixme it needs to tool bar size!
20947                 for (var i =0; i < this.toolbars.length;i++) {
20948                     // fixme - ask toolbars for heights?
20949                     tbh += this.toolbars[i].el.getHeight();
20950                     //if (this.toolbars[i].footer) {
20951                     //    tbh += this.toolbars[i].footer.el.getHeight();
20952                     //}
20953                 }
20954               
20955                 
20956                 
20957                 
20958                 
20959                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20960                 ah -= 5; // knock a few pixes off for look..
20961                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20962                 var eh = ah;
20963             }
20964         }
20965         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20966         this.editorcore.onResize(ew,eh);
20967         
20968     },
20969
20970     /**
20971      * Toggles the editor between standard and source edit mode.
20972      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20973      */
20974     toggleSourceEdit : function(sourceEditMode)
20975     {
20976         this.editorcore.toggleSourceEdit(sourceEditMode);
20977         
20978         if(this.editorcore.sourceEditMode){
20979             Roo.log('editor - showing textarea');
20980             
20981 //            Roo.log('in');
20982 //            Roo.log(this.syncValue());
20983             this.syncValue();
20984             this.inputEl().removeClass(['hide', 'x-hidden']);
20985             this.inputEl().dom.removeAttribute('tabIndex');
20986             this.inputEl().focus();
20987         }else{
20988             Roo.log('editor - hiding textarea');
20989 //            Roo.log('out')
20990 //            Roo.log(this.pushValue()); 
20991             this.pushValue();
20992             
20993             this.inputEl().addClass(['hide', 'x-hidden']);
20994             this.inputEl().dom.setAttribute('tabIndex', -1);
20995             //this.deferFocus();
20996         }
20997          
20998         if(this.resizable){
20999             this.setSize(this.wrap.getSize());
21000         }
21001         
21002         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21003     },
21004  
21005     // private (for BoxComponent)
21006     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21007
21008     // private (for BoxComponent)
21009     getResizeEl : function(){
21010         return this.wrap;
21011     },
21012
21013     // private (for BoxComponent)
21014     getPositionEl : function(){
21015         return this.wrap;
21016     },
21017
21018     // private
21019     initEvents : function(){
21020         this.originalValue = this.getValue();
21021     },
21022
21023 //    /**
21024 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21025 //     * @method
21026 //     */
21027 //    markInvalid : Roo.emptyFn,
21028 //    /**
21029 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21030 //     * @method
21031 //     */
21032 //    clearInvalid : Roo.emptyFn,
21033
21034     setValue : function(v){
21035         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21036         this.editorcore.pushValue();
21037     },
21038
21039      
21040     // private
21041     deferFocus : function(){
21042         this.focus.defer(10, this);
21043     },
21044
21045     // doc'ed in Field
21046     focus : function(){
21047         this.editorcore.focus();
21048         
21049     },
21050       
21051
21052     // private
21053     onDestroy : function(){
21054         
21055         
21056         
21057         if(this.rendered){
21058             
21059             for (var i =0; i < this.toolbars.length;i++) {
21060                 // fixme - ask toolbars for heights?
21061                 this.toolbars[i].onDestroy();
21062             }
21063             
21064             this.wrap.dom.innerHTML = '';
21065             this.wrap.remove();
21066         }
21067     },
21068
21069     // private
21070     onFirstFocus : function(){
21071         //Roo.log("onFirstFocus");
21072         this.editorcore.onFirstFocus();
21073          for (var i =0; i < this.toolbars.length;i++) {
21074             this.toolbars[i].onFirstFocus();
21075         }
21076         
21077     },
21078     
21079     // private
21080     syncValue : function()
21081     {   
21082         this.editorcore.syncValue();
21083     },
21084     
21085     pushValue : function()
21086     {   
21087         this.editorcore.pushValue();
21088     }
21089      
21090     
21091     // hide stuff that is not compatible
21092     /**
21093      * @event blur
21094      * @hide
21095      */
21096     /**
21097      * @event change
21098      * @hide
21099      */
21100     /**
21101      * @event focus
21102      * @hide
21103      */
21104     /**
21105      * @event specialkey
21106      * @hide
21107      */
21108     /**
21109      * @cfg {String} fieldClass @hide
21110      */
21111     /**
21112      * @cfg {String} focusClass @hide
21113      */
21114     /**
21115      * @cfg {String} autoCreate @hide
21116      */
21117     /**
21118      * @cfg {String} inputType @hide
21119      */
21120     /**
21121      * @cfg {String} invalidClass @hide
21122      */
21123     /**
21124      * @cfg {String} invalidText @hide
21125      */
21126     /**
21127      * @cfg {String} msgFx @hide
21128      */
21129     /**
21130      * @cfg {String} validateOnBlur @hide
21131      */
21132 });
21133  
21134     
21135    
21136    
21137    
21138       
21139 Roo.namespace('Roo.bootstrap.htmleditor');
21140 /**
21141  * @class Roo.bootstrap.HtmlEditorToolbar1
21142  * Basic Toolbar
21143  * 
21144  * Usage:
21145  *
21146  new Roo.bootstrap.HtmlEditor({
21147     ....
21148     toolbars : [
21149         new Roo.bootstrap.HtmlEditorToolbar1({
21150             disable : { fonts: 1 , format: 1, ..., ... , ...],
21151             btns : [ .... ]
21152         })
21153     }
21154      
21155  * 
21156  * @cfg {Object} disable List of elements to disable..
21157  * @cfg {Array} btns List of additional buttons.
21158  * 
21159  * 
21160  * NEEDS Extra CSS? 
21161  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21162  */
21163  
21164 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21165 {
21166     
21167     Roo.apply(this, config);
21168     
21169     // default disabled, based on 'good practice'..
21170     this.disable = this.disable || {};
21171     Roo.applyIf(this.disable, {
21172         fontSize : true,
21173         colors : true,
21174         specialElements : true
21175     });
21176     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21177     
21178     this.editor = config.editor;
21179     this.editorcore = config.editor.editorcore;
21180     
21181     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21182     
21183     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21184     // dont call parent... till later.
21185 }
21186 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21187      
21188     bar : true,
21189     
21190     editor : false,
21191     editorcore : false,
21192     
21193     
21194     formats : [
21195         "p" ,  
21196         "h1","h2","h3","h4","h5","h6", 
21197         "pre", "code", 
21198         "abbr", "acronym", "address", "cite", "samp", "var",
21199         'div','span'
21200     ],
21201     
21202     onRender : function(ct, position)
21203     {
21204        // Roo.log("Call onRender: " + this.xtype);
21205         
21206        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21207        Roo.log(this.el);
21208        this.el.dom.style.marginBottom = '0';
21209        var _this = this;
21210        var editorcore = this.editorcore;
21211        var editor= this.editor;
21212        
21213        var children = [];
21214        var btn = function(id,cmd , toggle, handler){
21215        
21216             var  event = toggle ? 'toggle' : 'click';
21217        
21218             var a = {
21219                 size : 'sm',
21220                 xtype: 'Button',
21221                 xns: Roo.bootstrap,
21222                 glyphicon : id,
21223                 cmd : id || cmd,
21224                 enableToggle:toggle !== false,
21225                 //html : 'submit'
21226                 pressed : toggle ? false : null,
21227                 listeners : {}
21228             };
21229             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21230                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21231             };
21232             children.push(a);
21233             return a;
21234        }
21235         
21236         var style = {
21237                 xtype: 'Button',
21238                 size : 'sm',
21239                 xns: Roo.bootstrap,
21240                 glyphicon : 'font',
21241                 //html : 'submit'
21242                 menu : {
21243                     xtype: 'Menu',
21244                     xns: Roo.bootstrap,
21245                     items:  []
21246                 }
21247         };
21248         Roo.each(this.formats, function(f) {
21249             style.menu.items.push({
21250                 xtype :'MenuItem',
21251                 xns: Roo.bootstrap,
21252                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21253                 tagname : f,
21254                 listeners : {
21255                     click : function()
21256                     {
21257                         editorcore.insertTag(this.tagname);
21258                         editor.focus();
21259                     }
21260                 }
21261                 
21262             });
21263         });
21264          children.push(style);   
21265             
21266             
21267         btn('bold',false,true);
21268         btn('italic',false,true);
21269         btn('align-left', 'justifyleft',true);
21270         btn('align-center', 'justifycenter',true);
21271         btn('align-right' , 'justifyright',true);
21272         btn('link', false, false, function(btn) {
21273             //Roo.log("create link?");
21274             var url = prompt(this.createLinkText, this.defaultLinkValue);
21275             if(url && url != 'http:/'+'/'){
21276                 this.editorcore.relayCmd('createlink', url);
21277             }
21278         }),
21279         btn('list','insertunorderedlist',true);
21280         btn('pencil', false,true, function(btn){
21281                 Roo.log(this);
21282                 
21283                 this.toggleSourceEdit(btn.pressed);
21284         });
21285         /*
21286         var cog = {
21287                 xtype: 'Button',
21288                 size : 'sm',
21289                 xns: Roo.bootstrap,
21290                 glyphicon : 'cog',
21291                 //html : 'submit'
21292                 menu : {
21293                     xtype: 'Menu',
21294                     xns: Roo.bootstrap,
21295                     items:  []
21296                 }
21297         };
21298         
21299         cog.menu.items.push({
21300             xtype :'MenuItem',
21301             xns: Roo.bootstrap,
21302             html : Clean styles,
21303             tagname : f,
21304             listeners : {
21305                 click : function()
21306                 {
21307                     editorcore.insertTag(this.tagname);
21308                     editor.focus();
21309                 }
21310             }
21311             
21312         });
21313        */
21314         
21315          
21316        this.xtype = 'NavSimplebar';
21317         
21318         for(var i=0;i< children.length;i++) {
21319             
21320             this.buttons.add(this.addxtypeChild(children[i]));
21321             
21322         }
21323         
21324         editor.on('editorevent', this.updateToolbar, this);
21325     },
21326     onBtnClick : function(id)
21327     {
21328        this.editorcore.relayCmd(id);
21329        this.editorcore.focus();
21330     },
21331     
21332     /**
21333      * Protected method that will not generally be called directly. It triggers
21334      * a toolbar update by reading the markup state of the current selection in the editor.
21335      */
21336     updateToolbar: function(){
21337
21338         if(!this.editorcore.activated){
21339             this.editor.onFirstFocus(); // is this neeed?
21340             return;
21341         }
21342
21343         var btns = this.buttons; 
21344         var doc = this.editorcore.doc;
21345         btns.get('bold').setActive(doc.queryCommandState('bold'));
21346         btns.get('italic').setActive(doc.queryCommandState('italic'));
21347         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21348         
21349         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21350         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21351         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21352         
21353         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21354         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21355          /*
21356         
21357         var ans = this.editorcore.getAllAncestors();
21358         if (this.formatCombo) {
21359             
21360             
21361             var store = this.formatCombo.store;
21362             this.formatCombo.setValue("");
21363             for (var i =0; i < ans.length;i++) {
21364                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21365                     // select it..
21366                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21367                     break;
21368                 }
21369             }
21370         }
21371         
21372         
21373         
21374         // hides menus... - so this cant be on a menu...
21375         Roo.bootstrap.MenuMgr.hideAll();
21376         */
21377         Roo.bootstrap.MenuMgr.hideAll();
21378         //this.editorsyncValue();
21379     },
21380     onFirstFocus: function() {
21381         this.buttons.each(function(item){
21382            item.enable();
21383         });
21384     },
21385     toggleSourceEdit : function(sourceEditMode){
21386         
21387           
21388         if(sourceEditMode){
21389             Roo.log("disabling buttons");
21390            this.buttons.each( function(item){
21391                 if(item.cmd != 'pencil'){
21392                     item.disable();
21393                 }
21394             });
21395           
21396         }else{
21397             Roo.log("enabling buttons");
21398             if(this.editorcore.initialized){
21399                 this.buttons.each( function(item){
21400                     item.enable();
21401                 });
21402             }
21403             
21404         }
21405         Roo.log("calling toggole on editor");
21406         // tell the editor that it's been pressed..
21407         this.editor.toggleSourceEdit(sourceEditMode);
21408        
21409     }
21410 });
21411
21412
21413
21414
21415
21416 /**
21417  * @class Roo.bootstrap.Table.AbstractSelectionModel
21418  * @extends Roo.util.Observable
21419  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21420  * implemented by descendant classes.  This class should not be directly instantiated.
21421  * @constructor
21422  */
21423 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21424     this.locked = false;
21425     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21426 };
21427
21428
21429 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21430     /** @ignore Called by the grid automatically. Do not call directly. */
21431     init : function(grid){
21432         this.grid = grid;
21433         this.initEvents();
21434     },
21435
21436     /**
21437      * Locks the selections.
21438      */
21439     lock : function(){
21440         this.locked = true;
21441     },
21442
21443     /**
21444      * Unlocks the selections.
21445      */
21446     unlock : function(){
21447         this.locked = false;
21448     },
21449
21450     /**
21451      * Returns true if the selections are locked.
21452      * @return {Boolean}
21453      */
21454     isLocked : function(){
21455         return this.locked;
21456     }
21457 });
21458 /**
21459  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21460  * @class Roo.bootstrap.Table.RowSelectionModel
21461  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21462  * It supports multiple selections and keyboard selection/navigation. 
21463  * @constructor
21464  * @param {Object} config
21465  */
21466
21467 Roo.bootstrap.Table.RowSelectionModel = function(config){
21468     Roo.apply(this, config);
21469     this.selections = new Roo.util.MixedCollection(false, function(o){
21470         return o.id;
21471     });
21472
21473     this.last = false;
21474     this.lastActive = false;
21475
21476     this.addEvents({
21477         /**
21478              * @event selectionchange
21479              * Fires when the selection changes
21480              * @param {SelectionModel} this
21481              */
21482             "selectionchange" : true,
21483         /**
21484              * @event afterselectionchange
21485              * Fires after the selection changes (eg. by key press or clicking)
21486              * @param {SelectionModel} this
21487              */
21488             "afterselectionchange" : true,
21489         /**
21490              * @event beforerowselect
21491              * Fires when a row is selected being selected, return false to cancel.
21492              * @param {SelectionModel} this
21493              * @param {Number} rowIndex The selected index
21494              * @param {Boolean} keepExisting False if other selections will be cleared
21495              */
21496             "beforerowselect" : true,
21497         /**
21498              * @event rowselect
21499              * Fires when a row is selected.
21500              * @param {SelectionModel} this
21501              * @param {Number} rowIndex The selected index
21502              * @param {Roo.data.Record} r The record
21503              */
21504             "rowselect" : true,
21505         /**
21506              * @event rowdeselect
21507              * Fires when a row is deselected.
21508              * @param {SelectionModel} this
21509              * @param {Number} rowIndex The selected index
21510              */
21511         "rowdeselect" : true
21512     });
21513     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21514     this.locked = false;
21515 };
21516
21517 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21518     /**
21519      * @cfg {Boolean} singleSelect
21520      * True to allow selection of only one row at a time (defaults to false)
21521      */
21522     singleSelect : false,
21523
21524     // private
21525     initEvents : function(){
21526
21527         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21528             this.grid.on("mousedown", this.handleMouseDown, this);
21529         }else{ // allow click to work like normal
21530             this.grid.on("rowclick", this.handleDragableRowClick, this);
21531         }
21532
21533         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21534             "up" : function(e){
21535                 if(!e.shiftKey){
21536                     this.selectPrevious(e.shiftKey);
21537                 }else if(this.last !== false && this.lastActive !== false){
21538                     var last = this.last;
21539                     this.selectRange(this.last,  this.lastActive-1);
21540                     this.grid.getView().focusRow(this.lastActive);
21541                     if(last !== false){
21542                         this.last = last;
21543                     }
21544                 }else{
21545                     this.selectFirstRow();
21546                 }
21547                 this.fireEvent("afterselectionchange", this);
21548             },
21549             "down" : function(e){
21550                 if(!e.shiftKey){
21551                     this.selectNext(e.shiftKey);
21552                 }else if(this.last !== false && this.lastActive !== false){
21553                     var last = this.last;
21554                     this.selectRange(this.last,  this.lastActive+1);
21555                     this.grid.getView().focusRow(this.lastActive);
21556                     if(last !== false){
21557                         this.last = last;
21558                     }
21559                 }else{
21560                     this.selectFirstRow();
21561                 }
21562                 this.fireEvent("afterselectionchange", this);
21563             },
21564             scope: this
21565         });
21566
21567         var view = this.grid.view;
21568         view.on("refresh", this.onRefresh, this);
21569         view.on("rowupdated", this.onRowUpdated, this);
21570         view.on("rowremoved", this.onRemove, this);
21571     },
21572
21573     // private
21574     onRefresh : function(){
21575         var ds = this.grid.dataSource, i, v = this.grid.view;
21576         var s = this.selections;
21577         s.each(function(r){
21578             if((i = ds.indexOfId(r.id)) != -1){
21579                 v.onRowSelect(i);
21580             }else{
21581                 s.remove(r);
21582             }
21583         });
21584     },
21585
21586     // private
21587     onRemove : function(v, index, r){
21588         this.selections.remove(r);
21589     },
21590
21591     // private
21592     onRowUpdated : function(v, index, r){
21593         if(this.isSelected(r)){
21594             v.onRowSelect(index);
21595         }
21596     },
21597
21598     /**
21599      * Select records.
21600      * @param {Array} records The records to select
21601      * @param {Boolean} keepExisting (optional) True to keep existing selections
21602      */
21603     selectRecords : function(records, keepExisting){
21604         if(!keepExisting){
21605             this.clearSelections();
21606         }
21607         var ds = this.grid.dataSource;
21608         for(var i = 0, len = records.length; i < len; i++){
21609             this.selectRow(ds.indexOf(records[i]), true);
21610         }
21611     },
21612
21613     /**
21614      * Gets the number of selected rows.
21615      * @return {Number}
21616      */
21617     getCount : function(){
21618         return this.selections.length;
21619     },
21620
21621     /**
21622      * Selects the first row in the grid.
21623      */
21624     selectFirstRow : function(){
21625         this.selectRow(0);
21626     },
21627
21628     /**
21629      * Select the last row.
21630      * @param {Boolean} keepExisting (optional) True to keep existing selections
21631      */
21632     selectLastRow : function(keepExisting){
21633         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21634     },
21635
21636     /**
21637      * Selects the row immediately following the last selected row.
21638      * @param {Boolean} keepExisting (optional) True to keep existing selections
21639      */
21640     selectNext : function(keepExisting){
21641         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21642             this.selectRow(this.last+1, keepExisting);
21643             this.grid.getView().focusRow(this.last);
21644         }
21645     },
21646
21647     /**
21648      * Selects the row that precedes the last selected row.
21649      * @param {Boolean} keepExisting (optional) True to keep existing selections
21650      */
21651     selectPrevious : function(keepExisting){
21652         if(this.last){
21653             this.selectRow(this.last-1, keepExisting);
21654             this.grid.getView().focusRow(this.last);
21655         }
21656     },
21657
21658     /**
21659      * Returns the selected records
21660      * @return {Array} Array of selected records
21661      */
21662     getSelections : function(){
21663         return [].concat(this.selections.items);
21664     },
21665
21666     /**
21667      * Returns the first selected record.
21668      * @return {Record}
21669      */
21670     getSelected : function(){
21671         return this.selections.itemAt(0);
21672     },
21673
21674
21675     /**
21676      * Clears all selections.
21677      */
21678     clearSelections : function(fast){
21679         if(this.locked) return;
21680         if(fast !== true){
21681             var ds = this.grid.dataSource;
21682             var s = this.selections;
21683             s.each(function(r){
21684                 this.deselectRow(ds.indexOfId(r.id));
21685             }, this);
21686             s.clear();
21687         }else{
21688             this.selections.clear();
21689         }
21690         this.last = false;
21691     },
21692
21693
21694     /**
21695      * Selects all rows.
21696      */
21697     selectAll : function(){
21698         if(this.locked) return;
21699         this.selections.clear();
21700         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21701             this.selectRow(i, true);
21702         }
21703     },
21704
21705     /**
21706      * Returns True if there is a selection.
21707      * @return {Boolean}
21708      */
21709     hasSelection : function(){
21710         return this.selections.length > 0;
21711     },
21712
21713     /**
21714      * Returns True if the specified row is selected.
21715      * @param {Number/Record} record The record or index of the record to check
21716      * @return {Boolean}
21717      */
21718     isSelected : function(index){
21719         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21720         return (r && this.selections.key(r.id) ? true : false);
21721     },
21722
21723     /**
21724      * Returns True if the specified record id is selected.
21725      * @param {String} id The id of record to check
21726      * @return {Boolean}
21727      */
21728     isIdSelected : function(id){
21729         return (this.selections.key(id) ? true : false);
21730     },
21731
21732     // private
21733     handleMouseDown : function(e, t){
21734         var view = this.grid.getView(), rowIndex;
21735         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21736             return;
21737         };
21738         if(e.shiftKey && this.last !== false){
21739             var last = this.last;
21740             this.selectRange(last, rowIndex, e.ctrlKey);
21741             this.last = last; // reset the last
21742             view.focusRow(rowIndex);
21743         }else{
21744             var isSelected = this.isSelected(rowIndex);
21745             if(e.button !== 0 && isSelected){
21746                 view.focusRow(rowIndex);
21747             }else if(e.ctrlKey && isSelected){
21748                 this.deselectRow(rowIndex);
21749             }else if(!isSelected){
21750                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21751                 view.focusRow(rowIndex);
21752             }
21753         }
21754         this.fireEvent("afterselectionchange", this);
21755     },
21756     // private
21757     handleDragableRowClick :  function(grid, rowIndex, e) 
21758     {
21759         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21760             this.selectRow(rowIndex, false);
21761             grid.view.focusRow(rowIndex);
21762              this.fireEvent("afterselectionchange", this);
21763         }
21764     },
21765     
21766     /**
21767      * Selects multiple rows.
21768      * @param {Array} rows Array of the indexes of the row to select
21769      * @param {Boolean} keepExisting (optional) True to keep existing selections
21770      */
21771     selectRows : function(rows, keepExisting){
21772         if(!keepExisting){
21773             this.clearSelections();
21774         }
21775         for(var i = 0, len = rows.length; i < len; i++){
21776             this.selectRow(rows[i], true);
21777         }
21778     },
21779
21780     /**
21781      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21782      * @param {Number} startRow The index of the first row in the range
21783      * @param {Number} endRow The index of the last row in the range
21784      * @param {Boolean} keepExisting (optional) True to retain existing selections
21785      */
21786     selectRange : function(startRow, endRow, keepExisting){
21787         if(this.locked) return;
21788         if(!keepExisting){
21789             this.clearSelections();
21790         }
21791         if(startRow <= endRow){
21792             for(var i = startRow; i <= endRow; i++){
21793                 this.selectRow(i, true);
21794             }
21795         }else{
21796             for(var i = startRow; i >= endRow; i--){
21797                 this.selectRow(i, true);
21798             }
21799         }
21800     },
21801
21802     /**
21803      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21804      * @param {Number} startRow The index of the first row in the range
21805      * @param {Number} endRow The index of the last row in the range
21806      */
21807     deselectRange : function(startRow, endRow, preventViewNotify){
21808         if(this.locked) return;
21809         for(var i = startRow; i <= endRow; i++){
21810             this.deselectRow(i, preventViewNotify);
21811         }
21812     },
21813
21814     /**
21815      * Selects a row.
21816      * @param {Number} row The index of the row to select
21817      * @param {Boolean} keepExisting (optional) True to keep existing selections
21818      */
21819     selectRow : function(index, keepExisting, preventViewNotify){
21820         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21821         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21822             if(!keepExisting || this.singleSelect){
21823                 this.clearSelections();
21824             }
21825             var r = this.grid.dataSource.getAt(index);
21826             this.selections.add(r);
21827             this.last = this.lastActive = index;
21828             if(!preventViewNotify){
21829                 this.grid.getView().onRowSelect(index);
21830             }
21831             this.fireEvent("rowselect", this, index, r);
21832             this.fireEvent("selectionchange", this);
21833         }
21834     },
21835
21836     /**
21837      * Deselects a row.
21838      * @param {Number} row The index of the row to deselect
21839      */
21840     deselectRow : function(index, preventViewNotify){
21841         if(this.locked) return;
21842         if(this.last == index){
21843             this.last = false;
21844         }
21845         if(this.lastActive == index){
21846             this.lastActive = false;
21847         }
21848         var r = this.grid.dataSource.getAt(index);
21849         this.selections.remove(r);
21850         if(!preventViewNotify){
21851             this.grid.getView().onRowDeselect(index);
21852         }
21853         this.fireEvent("rowdeselect", this, index);
21854         this.fireEvent("selectionchange", this);
21855     },
21856
21857     // private
21858     restoreLast : function(){
21859         if(this._last){
21860             this.last = this._last;
21861         }
21862     },
21863
21864     // private
21865     acceptsNav : function(row, col, cm){
21866         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21867     },
21868
21869     // private
21870     onEditorKey : function(field, e){
21871         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21872         if(k == e.TAB){
21873             e.stopEvent();
21874             ed.completeEdit();
21875             if(e.shiftKey){
21876                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21877             }else{
21878                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21879             }
21880         }else if(k == e.ENTER && !e.ctrlKey){
21881             e.stopEvent();
21882             ed.completeEdit();
21883             if(e.shiftKey){
21884                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21885             }else{
21886                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21887             }
21888         }else if(k == e.ESC){
21889             ed.cancelEdit();
21890         }
21891         if(newCell){
21892             g.startEditing(newCell[0], newCell[1]);
21893         }
21894     }
21895 });/*
21896  * Based on:
21897  * Ext JS Library 1.1.1
21898  * Copyright(c) 2006-2007, Ext JS, LLC.
21899  *
21900  * Originally Released Under LGPL - original licence link has changed is not relivant.
21901  *
21902  * Fork - LGPL
21903  * <script type="text/javascript">
21904  */
21905  
21906 /**
21907  * @class Roo.bootstrap.PagingToolbar
21908  * @extends Roo.bootstrap.NavSimplebar
21909  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21910  * @constructor
21911  * Create a new PagingToolbar
21912  * @param {Object} config The config object
21913  * @param {Roo.data.Store} store
21914  */
21915 Roo.bootstrap.PagingToolbar = function(config)
21916 {
21917     // old args format still supported... - xtype is prefered..
21918         // created from xtype...
21919     
21920     this.ds = config.dataSource;
21921     
21922     if (config.store && !this.ds) {
21923         this.store= Roo.factory(config.store, Roo.data);
21924         this.ds = this.store;
21925         this.ds.xmodule = this.xmodule || false;
21926     }
21927     
21928     this.toolbarItems = [];
21929     if (config.items) {
21930         this.toolbarItems = config.items;
21931     }
21932     
21933     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21934     
21935     this.cursor = 0;
21936     
21937     if (this.ds) { 
21938         this.bind(this.ds);
21939     }
21940     
21941     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21942     
21943 };
21944
21945 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21946     /**
21947      * @cfg {Roo.data.Store} dataSource
21948      * The underlying data store providing the paged data
21949      */
21950     /**
21951      * @cfg {String/HTMLElement/Element} container
21952      * container The id or element that will contain the toolbar
21953      */
21954     /**
21955      * @cfg {Boolean} displayInfo
21956      * True to display the displayMsg (defaults to false)
21957      */
21958     /**
21959      * @cfg {Number} pageSize
21960      * The number of records to display per page (defaults to 20)
21961      */
21962     pageSize: 20,
21963     /**
21964      * @cfg {String} displayMsg
21965      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21966      */
21967     displayMsg : 'Displaying {0} - {1} of {2}',
21968     /**
21969      * @cfg {String} emptyMsg
21970      * The message to display when no records are found (defaults to "No data to display")
21971      */
21972     emptyMsg : 'No data to display',
21973     /**
21974      * Customizable piece of the default paging text (defaults to "Page")
21975      * @type String
21976      */
21977     beforePageText : "Page",
21978     /**
21979      * Customizable piece of the default paging text (defaults to "of %0")
21980      * @type String
21981      */
21982     afterPageText : "of {0}",
21983     /**
21984      * Customizable piece of the default paging text (defaults to "First Page")
21985      * @type String
21986      */
21987     firstText : "First Page",
21988     /**
21989      * Customizable piece of the default paging text (defaults to "Previous Page")
21990      * @type String
21991      */
21992     prevText : "Previous Page",
21993     /**
21994      * Customizable piece of the default paging text (defaults to "Next Page")
21995      * @type String
21996      */
21997     nextText : "Next Page",
21998     /**
21999      * Customizable piece of the default paging text (defaults to "Last Page")
22000      * @type String
22001      */
22002     lastText : "Last Page",
22003     /**
22004      * Customizable piece of the default paging text (defaults to "Refresh")
22005      * @type String
22006      */
22007     refreshText : "Refresh",
22008
22009     buttons : false,
22010     // private
22011     onRender : function(ct, position) 
22012     {
22013         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22014         this.navgroup.parentId = this.id;
22015         this.navgroup.onRender(this.el, null);
22016         // add the buttons to the navgroup
22017         
22018         if(this.displayInfo){
22019             Roo.log(this.el.select('ul.navbar-nav',true).first());
22020             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22021             this.displayEl = this.el.select('.x-paging-info', true).first();
22022 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22023 //            this.displayEl = navel.el.select('span',true).first();
22024         }
22025         
22026         var _this = this;
22027         
22028         if(this.buttons){
22029             Roo.each(_this.buttons, function(e){ // this might need to use render????
22030                Roo.factory(e).onRender(_this.el, null);
22031             });
22032         }
22033             
22034         Roo.each(_this.toolbarItems, function(e) {
22035             _this.navgroup.addItem(e);
22036         });
22037         
22038         
22039         this.first = this.navgroup.addItem({
22040             tooltip: this.firstText,
22041             cls: "prev",
22042             icon : 'fa fa-backward',
22043             disabled: true,
22044             preventDefault: true,
22045             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22046         });
22047         
22048         this.prev =  this.navgroup.addItem({
22049             tooltip: this.prevText,
22050             cls: "prev",
22051             icon : 'fa fa-step-backward',
22052             disabled: true,
22053             preventDefault: true,
22054             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22055         });
22056     //this.addSeparator();
22057         
22058         
22059         var field = this.navgroup.addItem( {
22060             tagtype : 'span',
22061             cls : 'x-paging-position',
22062             
22063             html : this.beforePageText  +
22064                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22065                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22066          } ); //?? escaped?
22067         
22068         this.field = field.el.select('input', true).first();
22069         this.field.on("keydown", this.onPagingKeydown, this);
22070         this.field.on("focus", function(){this.dom.select();});
22071     
22072     
22073         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22074         //this.field.setHeight(18);
22075         //this.addSeparator();
22076         this.next = this.navgroup.addItem({
22077             tooltip: this.nextText,
22078             cls: "next",
22079             html : ' <i class="fa fa-step-forward">',
22080             disabled: true,
22081             preventDefault: true,
22082             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22083         });
22084         this.last = this.navgroup.addItem({
22085             tooltip: this.lastText,
22086             icon : 'fa fa-forward',
22087             cls: "next",
22088             disabled: true,
22089             preventDefault: true,
22090             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22091         });
22092     //this.addSeparator();
22093         this.loading = this.navgroup.addItem({
22094             tooltip: this.refreshText,
22095             icon: 'fa fa-refresh',
22096             preventDefault: true,
22097             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22098         });
22099         
22100     },
22101
22102     // private
22103     updateInfo : function(){
22104         if(this.displayEl){
22105             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22106             var msg = count == 0 ?
22107                 this.emptyMsg :
22108                 String.format(
22109                     this.displayMsg,
22110                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22111                 );
22112             this.displayEl.update(msg);
22113         }
22114     },
22115
22116     // private
22117     onLoad : function(ds, r, o){
22118        this.cursor = o.params ? o.params.start : 0;
22119        var d = this.getPageData(),
22120             ap = d.activePage,
22121             ps = d.pages;
22122         
22123        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22124        this.field.dom.value = ap;
22125        this.first.setDisabled(ap == 1);
22126        this.prev.setDisabled(ap == 1);
22127        this.next.setDisabled(ap == ps);
22128        this.last.setDisabled(ap == ps);
22129        this.loading.enable();
22130        this.updateInfo();
22131     },
22132
22133     // private
22134     getPageData : function(){
22135         var total = this.ds.getTotalCount();
22136         return {
22137             total : total,
22138             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22139             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22140         };
22141     },
22142
22143     // private
22144     onLoadError : function(){
22145         this.loading.enable();
22146     },
22147
22148     // private
22149     onPagingKeydown : function(e){
22150         var k = e.getKey();
22151         var d = this.getPageData();
22152         if(k == e.RETURN){
22153             var v = this.field.dom.value, pageNum;
22154             if(!v || isNaN(pageNum = parseInt(v, 10))){
22155                 this.field.dom.value = d.activePage;
22156                 return;
22157             }
22158             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22159             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22160             e.stopEvent();
22161         }
22162         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))
22163         {
22164           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22165           this.field.dom.value = pageNum;
22166           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22167           e.stopEvent();
22168         }
22169         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22170         {
22171           var v = this.field.dom.value, pageNum; 
22172           var increment = (e.shiftKey) ? 10 : 1;
22173           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22174             increment *= -1;
22175           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22176             this.field.dom.value = d.activePage;
22177             return;
22178           }
22179           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22180           {
22181             this.field.dom.value = parseInt(v, 10) + increment;
22182             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22183             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22184           }
22185           e.stopEvent();
22186         }
22187     },
22188
22189     // private
22190     beforeLoad : function(){
22191         if(this.loading){
22192             this.loading.disable();
22193         }
22194     },
22195
22196     // private
22197     onClick : function(which){
22198         
22199         var ds = this.ds;
22200         if (!ds) {
22201             return;
22202         }
22203         
22204         switch(which){
22205             case "first":
22206                 ds.load({params:{start: 0, limit: this.pageSize}});
22207             break;
22208             case "prev":
22209                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22210             break;
22211             case "next":
22212                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22213             break;
22214             case "last":
22215                 var total = ds.getTotalCount();
22216                 var extra = total % this.pageSize;
22217                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22218                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22219             break;
22220             case "refresh":
22221                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22222             break;
22223         }
22224     },
22225
22226     /**
22227      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22228      * @param {Roo.data.Store} store The data store to unbind
22229      */
22230     unbind : function(ds){
22231         ds.un("beforeload", this.beforeLoad, this);
22232         ds.un("load", this.onLoad, this);
22233         ds.un("loadexception", this.onLoadError, this);
22234         ds.un("remove", this.updateInfo, this);
22235         ds.un("add", this.updateInfo, this);
22236         this.ds = undefined;
22237     },
22238
22239     /**
22240      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22241      * @param {Roo.data.Store} store The data store to bind
22242      */
22243     bind : function(ds){
22244         ds.on("beforeload", this.beforeLoad, this);
22245         ds.on("load", this.onLoad, this);
22246         ds.on("loadexception", this.onLoadError, this);
22247         ds.on("remove", this.updateInfo, this);
22248         ds.on("add", this.updateInfo, this);
22249         this.ds = ds;
22250     }
22251 });/*
22252  * - LGPL
22253  *
22254  * element
22255  * 
22256  */
22257
22258 /**
22259  * @class Roo.bootstrap.MessageBar
22260  * @extends Roo.bootstrap.Component
22261  * Bootstrap MessageBar class
22262  * @cfg {String} html contents of the MessageBar
22263  * @cfg {String} weight (info | success | warning | danger) default info
22264  * @cfg {String} beforeClass insert the bar before the given class
22265  * @cfg {Boolean} closable (true | false) default false
22266  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22267  * 
22268  * @constructor
22269  * Create a new Element
22270  * @param {Object} config The config object
22271  */
22272
22273 Roo.bootstrap.MessageBar = function(config){
22274     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22275 };
22276
22277 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22278     
22279     html: '',
22280     weight: 'info',
22281     closable: false,
22282     fixed: false,
22283     beforeClass: 'bootstrap-sticky-wrap',
22284     
22285     getAutoCreate : function(){
22286         
22287         var cfg = {
22288             tag: 'div',
22289             cls: 'alert alert-dismissable alert-' + this.weight,
22290             cn: [
22291                 {
22292                     tag: 'span',
22293                     cls: 'message',
22294                     html: this.html || ''
22295                 }
22296             ]
22297         };
22298         
22299         if(this.fixed){
22300             cfg.cls += ' alert-messages-fixed';
22301         }
22302         
22303         if(this.closable){
22304             cfg.cn.push({
22305                 tag: 'button',
22306                 cls: 'close',
22307                 html: 'x'
22308             });
22309         }
22310         
22311         return cfg;
22312     },
22313     
22314     onRender : function(ct, position)
22315     {
22316         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22317         
22318         if(!this.el){
22319             var cfg = Roo.apply({},  this.getAutoCreate());
22320             cfg.id = Roo.id();
22321             
22322             if (this.cls) {
22323                 cfg.cls += ' ' + this.cls;
22324             }
22325             if (this.style) {
22326                 cfg.style = this.style;
22327             }
22328             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22329             
22330             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22331         }
22332         
22333         this.el.select('>button.close').on('click', this.hide, this);
22334         
22335     },
22336     
22337     show : function()
22338     {
22339         if (!this.rendered) {
22340             this.render();
22341         }
22342         
22343         this.el.show();
22344         
22345         this.fireEvent('show', this);
22346         
22347     },
22348     
22349     hide : function()
22350     {
22351         if (!this.rendered) {
22352             this.render();
22353         }
22354         
22355         this.el.hide();
22356         
22357         this.fireEvent('hide', this);
22358     },
22359     
22360     update : function()
22361     {
22362 //        var e = this.el.dom.firstChild;
22363 //        
22364 //        if(this.closable){
22365 //            e = e.nextSibling;
22366 //        }
22367 //        
22368 //        e.data = this.html || '';
22369
22370         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22371     }
22372    
22373 });
22374
22375  
22376
22377      /*
22378  * - LGPL
22379  *
22380  * Graph
22381  * 
22382  */
22383
22384
22385 /**
22386  * @class Roo.bootstrap.Graph
22387  * @extends Roo.bootstrap.Component
22388  * Bootstrap Graph class
22389 > Prameters
22390  -sm {number} sm 4
22391  -md {number} md 5
22392  @cfg {String} graphtype  bar | vbar | pie
22393  @cfg {number} g_x coodinator | centre x (pie)
22394  @cfg {number} g_y coodinator | centre y (pie)
22395  @cfg {number} g_r radius (pie)
22396  @cfg {number} g_height height of the chart (respected by all elements in the set)
22397  @cfg {number} g_width width of the chart (respected by all elements in the set)
22398  @cfg {Object} title The title of the chart
22399     
22400  -{Array}  values
22401  -opts (object) options for the chart 
22402      o {
22403      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22404      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22405      o vgutter (number)
22406      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.
22407      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22408      o to
22409      o stretch (boolean)
22410      o }
22411  -opts (object) options for the pie
22412      o{
22413      o cut
22414      o startAngle (number)
22415      o endAngle (number)
22416      } 
22417  *
22418  * @constructor
22419  * Create a new Input
22420  * @param {Object} config The config object
22421  */
22422
22423 Roo.bootstrap.Graph = function(config){
22424     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22425     
22426     this.addEvents({
22427         // img events
22428         /**
22429          * @event click
22430          * The img click event for the img.
22431          * @param {Roo.EventObject} e
22432          */
22433         "click" : true
22434     });
22435 };
22436
22437 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22438     
22439     sm: 4,
22440     md: 5,
22441     graphtype: 'bar',
22442     g_height: 250,
22443     g_width: 400,
22444     g_x: 50,
22445     g_y: 50,
22446     g_r: 30,
22447     opts:{
22448         //g_colors: this.colors,
22449         g_type: 'soft',
22450         g_gutter: '20%'
22451
22452     },
22453     title : false,
22454
22455     getAutoCreate : function(){
22456         
22457         var cfg = {
22458             tag: 'div',
22459             html : null
22460         };
22461         
22462         
22463         return  cfg;
22464     },
22465
22466     onRender : function(ct,position){
22467         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22468         this.raphael = Raphael(this.el.dom);
22469         
22470                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22471                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22472                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22473                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22474                 /*
22475                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22476                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22477                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22478                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22479                 
22480                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22481                 r.barchart(330, 10, 300, 220, data1);
22482                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22483                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22484                 */
22485                 
22486                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22487                 // r.barchart(30, 30, 560, 250,  xdata, {
22488                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22489                 //     axis : "0 0 1 1",
22490                 //     axisxlabels :  xdata
22491                 //     //yvalues : cols,
22492                    
22493                 // });
22494 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22495 //        
22496 //        this.load(null,xdata,{
22497 //                axis : "0 0 1 1",
22498 //                axisxlabels :  xdata
22499 //                });
22500
22501     },
22502
22503     load : function(graphtype,xdata,opts){
22504         this.raphael.clear();
22505         if(!graphtype) {
22506             graphtype = this.graphtype;
22507         }
22508         if(!opts){
22509             opts = this.opts;
22510         }
22511         var r = this.raphael,
22512             fin = function () {
22513                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22514             },
22515             fout = function () {
22516                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22517             },
22518             pfin = function() {
22519                 this.sector.stop();
22520                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22521
22522                 if (this.label) {
22523                     this.label[0].stop();
22524                     this.label[0].attr({ r: 7.5 });
22525                     this.label[1].attr({ "font-weight": 800 });
22526                 }
22527             },
22528             pfout = function() {
22529                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22530
22531                 if (this.label) {
22532                     this.label[0].animate({ r: 5 }, 500, "bounce");
22533                     this.label[1].attr({ "font-weight": 400 });
22534                 }
22535             };
22536
22537         switch(graphtype){
22538             case 'bar':
22539                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22540                 break;
22541             case 'hbar':
22542                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22543                 break;
22544             case 'pie':
22545 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22546 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22547 //            
22548                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22549                 
22550                 break;
22551
22552         }
22553         
22554         if(this.title){
22555             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22556         }
22557         
22558     },
22559     
22560     setTitle: function(o)
22561     {
22562         this.title = o;
22563     },
22564     
22565     initEvents: function() {
22566         
22567         if(!this.href){
22568             this.el.on('click', this.onClick, this);
22569         }
22570     },
22571     
22572     onClick : function(e)
22573     {
22574         Roo.log('img onclick');
22575         this.fireEvent('click', this, e);
22576     }
22577    
22578 });
22579
22580  
22581 /*
22582  * - LGPL
22583  *
22584  * numberBox
22585  * 
22586  */
22587 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22588
22589 /**
22590  * @class Roo.bootstrap.dash.NumberBox
22591  * @extends Roo.bootstrap.Component
22592  * Bootstrap NumberBox class
22593  * @cfg {String} headline Box headline
22594  * @cfg {String} content Box content
22595  * @cfg {String} icon Box icon
22596  * @cfg {String} footer Footer text
22597  * @cfg {String} fhref Footer href
22598  * 
22599  * @constructor
22600  * Create a new NumberBox
22601  * @param {Object} config The config object
22602  */
22603
22604
22605 Roo.bootstrap.dash.NumberBox = function(config){
22606     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22607     
22608 };
22609
22610 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22611     
22612     headline : '',
22613     content : '',
22614     icon : '',
22615     footer : '',
22616     fhref : '',
22617     ficon : '',
22618     
22619     getAutoCreate : function(){
22620         
22621         var cfg = {
22622             tag : 'div',
22623             cls : 'small-box ',
22624             cn : [
22625                 {
22626                     tag : 'div',
22627                     cls : 'inner',
22628                     cn :[
22629                         {
22630                             tag : 'h3',
22631                             cls : 'roo-headline',
22632                             html : this.headline
22633                         },
22634                         {
22635                             tag : 'p',
22636                             cls : 'roo-content',
22637                             html : this.content
22638                         }
22639                     ]
22640                 }
22641             ]
22642         };
22643         
22644         if(this.icon){
22645             cfg.cn.push({
22646                 tag : 'div',
22647                 cls : 'icon',
22648                 cn :[
22649                     {
22650                         tag : 'i',
22651                         cls : 'ion ' + this.icon
22652                     }
22653                 ]
22654             });
22655         }
22656         
22657         if(this.footer){
22658             var footer = {
22659                 tag : 'a',
22660                 cls : 'small-box-footer',
22661                 href : this.fhref || '#',
22662                 html : this.footer
22663             };
22664             
22665             cfg.cn.push(footer);
22666             
22667         }
22668         
22669         return  cfg;
22670     },
22671
22672     onRender : function(ct,position){
22673         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22674
22675
22676        
22677                 
22678     },
22679
22680     setHeadline: function (value)
22681     {
22682         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22683     },
22684     
22685     setFooter: function (value, href)
22686     {
22687         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22688         
22689         if(href){
22690             this.el.select('a.small-box-footer',true).first().attr('href', href);
22691         }
22692         
22693     },
22694
22695     setContent: function (value)
22696     {
22697         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22698     },
22699
22700     initEvents: function() 
22701     {   
22702         
22703     }
22704     
22705 });
22706
22707  
22708 /*
22709  * - LGPL
22710  *
22711  * TabBox
22712  * 
22713  */
22714 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22715
22716 /**
22717  * @class Roo.bootstrap.dash.TabBox
22718  * @extends Roo.bootstrap.Component
22719  * Bootstrap TabBox class
22720  * @cfg {String} title Title of the TabBox
22721  * @cfg {String} icon Icon of the TabBox
22722  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22723  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22724  * 
22725  * @constructor
22726  * Create a new TabBox
22727  * @param {Object} config The config object
22728  */
22729
22730
22731 Roo.bootstrap.dash.TabBox = function(config){
22732     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22733     this.addEvents({
22734         // raw events
22735         /**
22736          * @event addpane
22737          * When a pane is added
22738          * @param {Roo.bootstrap.dash.TabPane} pane
22739          */
22740         "addpane" : true,
22741         /**
22742          * @event activatepane
22743          * When a pane is activated
22744          * @param {Roo.bootstrap.dash.TabPane} pane
22745          */
22746         "activatepane" : true
22747         
22748          
22749     });
22750     
22751     this.panes = [];
22752 };
22753
22754 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22755
22756     title : '',
22757     icon : false,
22758     showtabs : true,
22759     tabScrollable : false,
22760     
22761     getChildContainer : function()
22762     {
22763         return this.el.select('.tab-content', true).first();
22764     },
22765     
22766     getAutoCreate : function(){
22767         
22768         var header = {
22769             tag: 'li',
22770             cls: 'pull-left header',
22771             html: this.title,
22772             cn : []
22773         };
22774         
22775         if(this.icon){
22776             header.cn.push({
22777                 tag: 'i',
22778                 cls: 'fa ' + this.icon
22779             });
22780         }
22781         
22782         var h = {
22783             tag: 'ul',
22784             cls: 'nav nav-tabs pull-right',
22785             cn: [
22786                 header
22787             ]
22788         };
22789         
22790         if(this.tabScrollable){
22791             h = {
22792                 tag: 'div',
22793                 cls: 'tab-header',
22794                 cn: [
22795                     {
22796                         tag: 'ul',
22797                         cls: 'nav nav-tabs pull-right',
22798                         cn: [
22799                             header
22800                         ]
22801                     }
22802                 ]
22803             };
22804         }
22805         
22806         var cfg = {
22807             tag: 'div',
22808             cls: 'nav-tabs-custom',
22809             cn: [
22810                 h,
22811                 {
22812                     tag: 'div',
22813                     cls: 'tab-content no-padding',
22814                     cn: []
22815                 }
22816             ]
22817         };
22818
22819         return  cfg;
22820     },
22821     initEvents : function()
22822     {
22823         //Roo.log('add add pane handler');
22824         this.on('addpane', this.onAddPane, this);
22825     },
22826      /**
22827      * Updates the box title
22828      * @param {String} html to set the title to.
22829      */
22830     setTitle : function(value)
22831     {
22832         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22833     },
22834     onAddPane : function(pane)
22835     {
22836         this.panes.push(pane);
22837         //Roo.log('addpane');
22838         //Roo.log(pane);
22839         // tabs are rendere left to right..
22840         if(!this.showtabs){
22841             return;
22842         }
22843         
22844         var ctr = this.el.select('.nav-tabs', true).first();
22845          
22846          
22847         var existing = ctr.select('.nav-tab',true);
22848         var qty = existing.getCount();;
22849         
22850         
22851         var tab = ctr.createChild({
22852             tag : 'li',
22853             cls : 'nav-tab' + (qty ? '' : ' active'),
22854             cn : [
22855                 {
22856                     tag : 'a',
22857                     href:'#',
22858                     html : pane.title
22859                 }
22860             ]
22861         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22862         pane.tab = tab;
22863         
22864         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22865         if (!qty) {
22866             pane.el.addClass('active');
22867         }
22868         
22869                 
22870     },
22871     onTabClick : function(ev,un,ob,pane)
22872     {
22873         //Roo.log('tab - prev default');
22874         ev.preventDefault();
22875         
22876         
22877         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22878         pane.tab.addClass('active');
22879         //Roo.log(pane.title);
22880         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22881         // technically we should have a deactivate event.. but maybe add later.
22882         // and it should not de-activate the selected tab...
22883         this.fireEvent('activatepane', pane);
22884         pane.el.addClass('active');
22885         pane.fireEvent('activate');
22886         
22887         
22888     },
22889     
22890     getActivePane : function()
22891     {
22892         var r = false;
22893         Roo.each(this.panes, function(p) {
22894             if(p.el.hasClass('active')){
22895                 r = p;
22896                 return false;
22897             }
22898             
22899             return;
22900         });
22901         
22902         return r;
22903     }
22904     
22905     
22906 });
22907
22908  
22909 /*
22910  * - LGPL
22911  *
22912  * Tab pane
22913  * 
22914  */
22915 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22916 /**
22917  * @class Roo.bootstrap.TabPane
22918  * @extends Roo.bootstrap.Component
22919  * Bootstrap TabPane class
22920  * @cfg {Boolean} active (false | true) Default false
22921  * @cfg {String} title title of panel
22922
22923  * 
22924  * @constructor
22925  * Create a new TabPane
22926  * @param {Object} config The config object
22927  */
22928
22929 Roo.bootstrap.dash.TabPane = function(config){
22930     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22931     
22932     this.addEvents({
22933         // raw events
22934         /**
22935          * @event activate
22936          * When a pane is activated
22937          * @param {Roo.bootstrap.dash.TabPane} pane
22938          */
22939         "activate" : true
22940          
22941     });
22942 };
22943
22944 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22945     
22946     active : false,
22947     title : '',
22948     
22949     // the tabBox that this is attached to.
22950     tab : false,
22951      
22952     getAutoCreate : function() 
22953     {
22954         var cfg = {
22955             tag: 'div',
22956             cls: 'tab-pane'
22957         };
22958         
22959         if(this.active){
22960             cfg.cls += ' active';
22961         }
22962         
22963         return cfg;
22964     },
22965     initEvents  : function()
22966     {
22967         //Roo.log('trigger add pane handler');
22968         this.parent().fireEvent('addpane', this)
22969     },
22970     
22971      /**
22972      * Updates the tab title 
22973      * @param {String} html to set the title to.
22974      */
22975     setTitle: function(str)
22976     {
22977         if (!this.tab) {
22978             return;
22979         }
22980         this.title = str;
22981         this.tab.select('a', true).first().dom.innerHTML = str;
22982         
22983     }
22984     
22985     
22986     
22987 });
22988
22989  
22990
22991
22992  /*
22993  * - LGPL
22994  *
22995  * menu
22996  * 
22997  */
22998 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22999
23000 /**
23001  * @class Roo.bootstrap.menu.Menu
23002  * @extends Roo.bootstrap.Component
23003  * Bootstrap Menu class - container for Menu
23004  * @cfg {String} html Text of the menu
23005  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23006  * @cfg {String} icon Font awesome icon
23007  * @cfg {String} pos Menu align to (top | bottom) default bottom
23008  * 
23009  * 
23010  * @constructor
23011  * Create a new Menu
23012  * @param {Object} config The config object
23013  */
23014
23015
23016 Roo.bootstrap.menu.Menu = function(config){
23017     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23018     
23019     this.addEvents({
23020         /**
23021          * @event beforeshow
23022          * Fires before this menu is displayed
23023          * @param {Roo.bootstrap.menu.Menu} this
23024          */
23025         beforeshow : true,
23026         /**
23027          * @event beforehide
23028          * Fires before this menu is hidden
23029          * @param {Roo.bootstrap.menu.Menu} this
23030          */
23031         beforehide : true,
23032         /**
23033          * @event show
23034          * Fires after this menu is displayed
23035          * @param {Roo.bootstrap.menu.Menu} this
23036          */
23037         show : true,
23038         /**
23039          * @event hide
23040          * Fires after this menu is hidden
23041          * @param {Roo.bootstrap.menu.Menu} this
23042          */
23043         hide : true,
23044         /**
23045          * @event click
23046          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23047          * @param {Roo.bootstrap.menu.Menu} this
23048          * @param {Roo.EventObject} e
23049          */
23050         click : true
23051     });
23052     
23053 };
23054
23055 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23056     
23057     submenu : false,
23058     html : '',
23059     weight : 'default',
23060     icon : false,
23061     pos : 'bottom',
23062     
23063     
23064     getChildContainer : function() {
23065         if(this.isSubMenu){
23066             return this.el;
23067         }
23068         
23069         return this.el.select('ul.dropdown-menu', true).first();  
23070     },
23071     
23072     getAutoCreate : function()
23073     {
23074         var text = [
23075             {
23076                 tag : 'span',
23077                 cls : 'roo-menu-text',
23078                 html : this.html
23079             }
23080         ];
23081         
23082         if(this.icon){
23083             text.unshift({
23084                 tag : 'i',
23085                 cls : 'fa ' + this.icon
23086             })
23087         }
23088         
23089         
23090         var cfg = {
23091             tag : 'div',
23092             cls : 'btn-group',
23093             cn : [
23094                 {
23095                     tag : 'button',
23096                     cls : 'dropdown-button btn btn-' + this.weight,
23097                     cn : text
23098                 },
23099                 {
23100                     tag : 'button',
23101                     cls : 'dropdown-toggle btn btn-' + this.weight,
23102                     cn : [
23103                         {
23104                             tag : 'span',
23105                             cls : 'caret'
23106                         }
23107                     ]
23108                 },
23109                 {
23110                     tag : 'ul',
23111                     cls : 'dropdown-menu'
23112                 }
23113             ]
23114             
23115         };
23116         
23117         if(this.pos == 'top'){
23118             cfg.cls += ' dropup';
23119         }
23120         
23121         if(this.isSubMenu){
23122             cfg = {
23123                 tag : 'ul',
23124                 cls : 'dropdown-menu'
23125             }
23126         }
23127         
23128         return cfg;
23129     },
23130     
23131     onRender : function(ct, position)
23132     {
23133         this.isSubMenu = ct.hasClass('dropdown-submenu');
23134         
23135         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23136     },
23137     
23138     initEvents : function() 
23139     {
23140         if(this.isSubMenu){
23141             return;
23142         }
23143         
23144         this.hidden = true;
23145         
23146         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23147         this.triggerEl.on('click', this.onTriggerPress, this);
23148         
23149         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23150         this.buttonEl.on('click', this.onClick, this);
23151         
23152     },
23153     
23154     list : function()
23155     {
23156         if(this.isSubMenu){
23157             return this.el;
23158         }
23159         
23160         return this.el.select('ul.dropdown-menu', true).first();
23161     },
23162     
23163     onClick : function(e)
23164     {
23165         this.fireEvent("click", this, e);
23166     },
23167     
23168     onTriggerPress  : function(e)
23169     {   
23170         if (this.isVisible()) {
23171             this.hide();
23172         } else {
23173             this.show();
23174         }
23175     },
23176     
23177     isVisible : function(){
23178         return !this.hidden;
23179     },
23180     
23181     show : function()
23182     {
23183         this.fireEvent("beforeshow", this);
23184         
23185         this.hidden = false;
23186         this.el.addClass('open');
23187         
23188         Roo.get(document).on("mouseup", this.onMouseUp, this);
23189         
23190         this.fireEvent("show", this);
23191         
23192         
23193     },
23194     
23195     hide : function()
23196     {
23197         this.fireEvent("beforehide", this);
23198         
23199         this.hidden = true;
23200         this.el.removeClass('open');
23201         
23202         Roo.get(document).un("mouseup", this.onMouseUp);
23203         
23204         this.fireEvent("hide", this);
23205     },
23206     
23207     onMouseUp : function()
23208     {
23209         this.hide();
23210     }
23211     
23212 });
23213
23214  
23215  /*
23216  * - LGPL
23217  *
23218  * menu item
23219  * 
23220  */
23221 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23222
23223 /**
23224  * @class Roo.bootstrap.menu.Item
23225  * @extends Roo.bootstrap.Component
23226  * Bootstrap MenuItem class
23227  * @cfg {Boolean} submenu (true | false) default false
23228  * @cfg {String} html text of the item
23229  * @cfg {String} href the link
23230  * @cfg {Boolean} disable (true | false) default false
23231  * @cfg {Boolean} preventDefault (true | false) default true
23232  * @cfg {String} icon Font awesome icon
23233  * @cfg {String} pos Submenu align to (left | right) default right 
23234  * 
23235  * 
23236  * @constructor
23237  * Create a new Item
23238  * @param {Object} config The config object
23239  */
23240
23241
23242 Roo.bootstrap.menu.Item = function(config){
23243     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23244     this.addEvents({
23245         /**
23246          * @event mouseover
23247          * Fires when the mouse is hovering over this menu
23248          * @param {Roo.bootstrap.menu.Item} this
23249          * @param {Roo.EventObject} e
23250          */
23251         mouseover : true,
23252         /**
23253          * @event mouseout
23254          * Fires when the mouse exits this menu
23255          * @param {Roo.bootstrap.menu.Item} this
23256          * @param {Roo.EventObject} e
23257          */
23258         mouseout : true,
23259         // raw events
23260         /**
23261          * @event click
23262          * The raw click event for the entire grid.
23263          * @param {Roo.EventObject} e
23264          */
23265         click : true
23266     });
23267 };
23268
23269 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23270     
23271     submenu : false,
23272     href : '',
23273     html : '',
23274     preventDefault: true,
23275     disable : false,
23276     icon : false,
23277     pos : 'right',
23278     
23279     getAutoCreate : function()
23280     {
23281         var text = [
23282             {
23283                 tag : 'span',
23284                 cls : 'roo-menu-item-text',
23285                 html : this.html
23286             }
23287         ];
23288         
23289         if(this.icon){
23290             text.unshift({
23291                 tag : 'i',
23292                 cls : 'fa ' + this.icon
23293             })
23294         }
23295         
23296         var cfg = {
23297             tag : 'li',
23298             cn : [
23299                 {
23300                     tag : 'a',
23301                     href : this.href || '#',
23302                     cn : text
23303                 }
23304             ]
23305         };
23306         
23307         if(this.disable){
23308             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23309         }
23310         
23311         if(this.submenu){
23312             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23313             
23314             if(this.pos == 'left'){
23315                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23316             }
23317         }
23318         
23319         return cfg;
23320     },
23321     
23322     initEvents : function() 
23323     {
23324         this.el.on('mouseover', this.onMouseOver, this);
23325         this.el.on('mouseout', this.onMouseOut, this);
23326         
23327         this.el.select('a', true).first().on('click', this.onClick, this);
23328         
23329     },
23330     
23331     onClick : function(e)
23332     {
23333         if(this.preventDefault){
23334             e.preventDefault();
23335         }
23336         
23337         this.fireEvent("click", this, e);
23338     },
23339     
23340     onMouseOver : function(e)
23341     {
23342         if(this.submenu && this.pos == 'left'){
23343             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23344         }
23345         
23346         this.fireEvent("mouseover", this, e);
23347     },
23348     
23349     onMouseOut : function(e)
23350     {
23351         this.fireEvent("mouseout", this, e);
23352     }
23353 });
23354
23355  
23356
23357  /*
23358  * - LGPL
23359  *
23360  * menu separator
23361  * 
23362  */
23363 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23364
23365 /**
23366  * @class Roo.bootstrap.menu.Separator
23367  * @extends Roo.bootstrap.Component
23368  * Bootstrap Separator class
23369  * 
23370  * @constructor
23371  * Create a new Separator
23372  * @param {Object} config The config object
23373  */
23374
23375
23376 Roo.bootstrap.menu.Separator = function(config){
23377     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23378 };
23379
23380 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23381     
23382     getAutoCreate : function(){
23383         var cfg = {
23384             tag : 'li',
23385             cls: 'divider'
23386         };
23387         
23388         return cfg;
23389     }
23390    
23391 });
23392
23393  
23394
23395  /*
23396  * - LGPL
23397  *
23398  * Tooltip
23399  * 
23400  */
23401
23402 /**
23403  * @class Roo.bootstrap.Tooltip
23404  * Bootstrap Tooltip class
23405  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23406  * to determine which dom element triggers the tooltip.
23407  * 
23408  * It needs to add support for additional attributes like tooltip-position
23409  * 
23410  * @constructor
23411  * Create a new Toolti
23412  * @param {Object} config The config object
23413  */
23414
23415 Roo.bootstrap.Tooltip = function(config){
23416     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23417 };
23418
23419 Roo.apply(Roo.bootstrap.Tooltip, {
23420     /**
23421      * @function init initialize tooltip monitoring.
23422      * @static
23423      */
23424     currentEl : false,
23425     currentTip : false,
23426     currentRegion : false,
23427     
23428     //  init : delay?
23429     
23430     init : function()
23431     {
23432         Roo.get(document).on('mouseover', this.enter ,this);
23433         Roo.get(document).on('mouseout', this.leave, this);
23434          
23435         
23436         this.currentTip = new Roo.bootstrap.Tooltip();
23437     },
23438     
23439     enter : function(ev)
23440     {
23441         var dom = ev.getTarget();
23442         
23443         //Roo.log(['enter',dom]);
23444         var el = Roo.fly(dom);
23445         if (this.currentEl) {
23446             //Roo.log(dom);
23447             //Roo.log(this.currentEl);
23448             //Roo.log(this.currentEl.contains(dom));
23449             if (this.currentEl == el) {
23450                 return;
23451             }
23452             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23453                 return;
23454             }
23455
23456         }
23457         
23458         if (this.currentTip.el) {
23459             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23460         }    
23461         //Roo.log(ev);
23462         var bindEl = el;
23463         
23464         // you can not look for children, as if el is the body.. then everythign is the child..
23465         if (!el.attr('tooltip')) { //
23466             if (!el.select("[tooltip]").elements.length) {
23467                 return;
23468             }
23469             // is the mouse over this child...?
23470             bindEl = el.select("[tooltip]").first();
23471             var xy = ev.getXY();
23472             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23473                 //Roo.log("not in region.");
23474                 return;
23475             }
23476             //Roo.log("child element over..");
23477             
23478         }
23479         this.currentEl = bindEl;
23480         this.currentTip.bind(bindEl);
23481         this.currentRegion = Roo.lib.Region.getRegion(dom);
23482         this.currentTip.enter();
23483         
23484     },
23485     leave : function(ev)
23486     {
23487         var dom = ev.getTarget();
23488         //Roo.log(['leave',dom]);
23489         if (!this.currentEl) {
23490             return;
23491         }
23492         
23493         
23494         if (dom != this.currentEl.dom) {
23495             return;
23496         }
23497         var xy = ev.getXY();
23498         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23499             return;
23500         }
23501         // only activate leave if mouse cursor is outside... bounding box..
23502         
23503         
23504         
23505         
23506         if (this.currentTip) {
23507             this.currentTip.leave();
23508         }
23509         //Roo.log('clear currentEl');
23510         this.currentEl = false;
23511         
23512         
23513     },
23514     alignment : {
23515         'left' : ['r-l', [-2,0], 'right'],
23516         'right' : ['l-r', [2,0], 'left'],
23517         'bottom' : ['t-b', [0,2], 'top'],
23518         'top' : [ 'b-t', [0,-2], 'bottom']
23519     }
23520     
23521 });
23522
23523
23524 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23525     
23526     
23527     bindEl : false,
23528     
23529     delay : null, // can be { show : 300 , hide: 500}
23530     
23531     timeout : null,
23532     
23533     hoverState : null, //???
23534     
23535     placement : 'bottom', 
23536     
23537     getAutoCreate : function(){
23538     
23539         var cfg = {
23540            cls : 'tooltip',
23541            role : 'tooltip',
23542            cn : [
23543                 {
23544                     cls : 'tooltip-arrow'
23545                 },
23546                 {
23547                     cls : 'tooltip-inner'
23548                 }
23549            ]
23550         };
23551         
23552         return cfg;
23553     },
23554     bind : function(el)
23555     {
23556         this.bindEl = el;
23557     },
23558       
23559     
23560     enter : function () {
23561        
23562         if (this.timeout != null) {
23563             clearTimeout(this.timeout);
23564         }
23565         
23566         this.hoverState = 'in';
23567          //Roo.log("enter - show");
23568         if (!this.delay || !this.delay.show) {
23569             this.show();
23570             return;
23571         }
23572         var _t = this;
23573         this.timeout = setTimeout(function () {
23574             if (_t.hoverState == 'in') {
23575                 _t.show();
23576             }
23577         }, this.delay.show);
23578     },
23579     leave : function()
23580     {
23581         clearTimeout(this.timeout);
23582     
23583         this.hoverState = 'out';
23584          if (!this.delay || !this.delay.hide) {
23585             this.hide();
23586             return;
23587         }
23588        
23589         var _t = this;
23590         this.timeout = setTimeout(function () {
23591             //Roo.log("leave - timeout");
23592             
23593             if (_t.hoverState == 'out') {
23594                 _t.hide();
23595                 Roo.bootstrap.Tooltip.currentEl = false;
23596             }
23597         }, delay);
23598     },
23599     
23600     show : function ()
23601     {
23602         if (!this.el) {
23603             this.render(document.body);
23604         }
23605         // set content.
23606         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23607         
23608         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23609         
23610         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23611         
23612         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23613         
23614         var placement = typeof this.placement == 'function' ?
23615             this.placement.call(this, this.el, on_el) :
23616             this.placement;
23617             
23618         var autoToken = /\s?auto?\s?/i;
23619         var autoPlace = autoToken.test(placement);
23620         if (autoPlace) {
23621             placement = placement.replace(autoToken, '') || 'top';
23622         }
23623         
23624         //this.el.detach()
23625         //this.el.setXY([0,0]);
23626         this.el.show();
23627         //this.el.dom.style.display='block';
23628         
23629         //this.el.appendTo(on_el);
23630         
23631         var p = this.getPosition();
23632         var box = this.el.getBox();
23633         
23634         if (autoPlace) {
23635             // fixme..
23636         }
23637         
23638         var align = Roo.bootstrap.Tooltip.alignment[placement];
23639         
23640         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23641         
23642         if(placement == 'top' || placement == 'bottom'){
23643             if(xy[0] < 0){
23644                 placement = 'right';
23645             }
23646             
23647             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23648                 placement = 'left';
23649             }
23650         }
23651         
23652         align = Roo.bootstrap.Tooltip.alignment[placement];
23653         
23654         this.el.alignTo(this.bindEl, align[0],align[1]);
23655         //var arrow = this.el.select('.arrow',true).first();
23656         //arrow.set(align[2], 
23657         
23658         this.el.addClass(placement);
23659         
23660         this.el.addClass('in fade');
23661         
23662         this.hoverState = null;
23663         
23664         if (this.el.hasClass('fade')) {
23665             // fade it?
23666         }
23667         
23668     },
23669     hide : function()
23670     {
23671          
23672         if (!this.el) {
23673             return;
23674         }
23675         //this.el.setXY([0,0]);
23676         this.el.removeClass('in');
23677         //this.el.hide();
23678         
23679     }
23680     
23681 });
23682  
23683
23684  /*
23685  * - LGPL
23686  *
23687  * Location Picker
23688  * 
23689  */
23690
23691 /**
23692  * @class Roo.bootstrap.LocationPicker
23693  * @extends Roo.bootstrap.Component
23694  * Bootstrap LocationPicker class
23695  * @cfg {Number} latitude Position when init default 0
23696  * @cfg {Number} longitude Position when init default 0
23697  * @cfg {Number} zoom default 15
23698  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23699  * @cfg {Boolean} mapTypeControl default false
23700  * @cfg {Boolean} disableDoubleClickZoom default false
23701  * @cfg {Boolean} scrollwheel default true
23702  * @cfg {Boolean} streetViewControl default false
23703  * @cfg {Number} radius default 0
23704  * @cfg {String} locationName
23705  * @cfg {Boolean} draggable default true
23706  * @cfg {Boolean} enableAutocomplete default false
23707  * @cfg {Boolean} enableReverseGeocode default true
23708  * @cfg {String} markerTitle
23709  * 
23710  * @constructor
23711  * Create a new LocationPicker
23712  * @param {Object} config The config object
23713  */
23714
23715
23716 Roo.bootstrap.LocationPicker = function(config){
23717     
23718     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23719     
23720     this.addEvents({
23721         /**
23722          * @event initial
23723          * Fires when the picker initialized.
23724          * @param {Roo.bootstrap.LocationPicker} this
23725          * @param {Google Location} location
23726          */
23727         initial : true,
23728         /**
23729          * @event positionchanged
23730          * Fires when the picker position changed.
23731          * @param {Roo.bootstrap.LocationPicker} this
23732          * @param {Google Location} location
23733          */
23734         positionchanged : true,
23735         /**
23736          * @event resize
23737          * Fires when the map resize.
23738          * @param {Roo.bootstrap.LocationPicker} this
23739          */
23740         resize : true,
23741         /**
23742          * @event show
23743          * Fires when the map show.
23744          * @param {Roo.bootstrap.LocationPicker} this
23745          */
23746         show : true,
23747         /**
23748          * @event hide
23749          * Fires when the map hide.
23750          * @param {Roo.bootstrap.LocationPicker} this
23751          */
23752         hide : true,
23753         /**
23754          * @event mapClick
23755          * Fires when click the map.
23756          * @param {Roo.bootstrap.LocationPicker} this
23757          * @param {Map event} e
23758          */
23759         mapClick : true,
23760         /**
23761          * @event mapRightClick
23762          * Fires when right click the map.
23763          * @param {Roo.bootstrap.LocationPicker} this
23764          * @param {Map event} e
23765          */
23766         mapRightClick : true,
23767         /**
23768          * @event markerClick
23769          * Fires when click the marker.
23770          * @param {Roo.bootstrap.LocationPicker} this
23771          * @param {Map event} e
23772          */
23773         markerClick : true,
23774         /**
23775          * @event markerRightClick
23776          * Fires when right click the marker.
23777          * @param {Roo.bootstrap.LocationPicker} this
23778          * @param {Map event} e
23779          */
23780         markerRightClick : true,
23781         /**
23782          * @event OverlayViewDraw
23783          * Fires when OverlayView Draw
23784          * @param {Roo.bootstrap.LocationPicker} this
23785          */
23786         OverlayViewDraw : true,
23787         /**
23788          * @event OverlayViewOnAdd
23789          * Fires when OverlayView Draw
23790          * @param {Roo.bootstrap.LocationPicker} this
23791          */
23792         OverlayViewOnAdd : true,
23793         /**
23794          * @event OverlayViewOnRemove
23795          * Fires when OverlayView Draw
23796          * @param {Roo.bootstrap.LocationPicker} this
23797          */
23798         OverlayViewOnRemove : true,
23799         /**
23800          * @event OverlayViewShow
23801          * Fires when OverlayView Draw
23802          * @param {Roo.bootstrap.LocationPicker} this
23803          * @param {Pixel} cpx
23804          */
23805         OverlayViewShow : true,
23806         /**
23807          * @event OverlayViewHide
23808          * Fires when OverlayView Draw
23809          * @param {Roo.bootstrap.LocationPicker} this
23810          */
23811         OverlayViewHide : true,
23812         /**
23813          * @event loadexception
23814          * Fires when load google lib failed.
23815          * @param {Roo.bootstrap.LocationPicker} this
23816          */
23817         loadexception : true
23818     });
23819         
23820 };
23821
23822 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23823     
23824     gMapContext: false,
23825     
23826     latitude: 0,
23827     longitude: 0,
23828     zoom: 15,
23829     mapTypeId: false,
23830     mapTypeControl: false,
23831     disableDoubleClickZoom: false,
23832     scrollwheel: true,
23833     streetViewControl: false,
23834     radius: 0,
23835     locationName: '',
23836     draggable: true,
23837     enableAutocomplete: false,
23838     enableReverseGeocode: true,
23839     markerTitle: '',
23840     
23841     getAutoCreate: function()
23842     {
23843
23844         var cfg = {
23845             tag: 'div',
23846             cls: 'roo-location-picker'
23847         };
23848         
23849         return cfg
23850     },
23851     
23852     initEvents: function(ct, position)
23853     {       
23854         if(!this.el.getWidth() || this.isApplied()){
23855             return;
23856         }
23857         
23858         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23859         
23860         this.initial();
23861     },
23862     
23863     initial: function()
23864     {
23865         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
23866             this.fireEvent('loadexception', this);
23867             return;
23868         }
23869         
23870         if(!this.mapTypeId){
23871             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23872         }
23873         
23874         this.gMapContext = this.GMapContext();
23875         
23876         this.initOverlayView();
23877         
23878         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23879         
23880         var _this = this;
23881                 
23882         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23883             _this.setPosition(_this.gMapContext.marker.position);
23884         });
23885         
23886         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23887             _this.fireEvent('mapClick', this, event);
23888             
23889         });
23890
23891         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23892             _this.fireEvent('mapRightClick', this, event);
23893             
23894         });
23895         
23896         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23897             _this.fireEvent('markerClick', this, event);
23898             
23899         });
23900
23901         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23902             _this.fireEvent('markerRightClick', this, event);
23903             
23904         });
23905         
23906         this.setPosition(this.gMapContext.location);
23907         
23908         this.fireEvent('initial', this, this.gMapContext.location);
23909     },
23910     
23911     initOverlayView: function()
23912     {
23913         var _this = this;
23914         
23915         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23916             
23917             draw: function()
23918             {
23919                 _this.fireEvent('OverlayViewDraw', _this);
23920             },
23921             
23922             onAdd: function()
23923             {
23924                 _this.fireEvent('OverlayViewOnAdd', _this);
23925             },
23926             
23927             onRemove: function()
23928             {
23929                 _this.fireEvent('OverlayViewOnRemove', _this);
23930             },
23931             
23932             show: function(cpx)
23933             {
23934                 _this.fireEvent('OverlayViewShow', _this, cpx);
23935             },
23936             
23937             hide: function()
23938             {
23939                 _this.fireEvent('OverlayViewHide', _this);
23940             }
23941             
23942         });
23943     },
23944     
23945     fromLatLngToContainerPixel: function(event)
23946     {
23947         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23948     },
23949     
23950     isApplied: function() 
23951     {
23952         return this.getGmapContext() == false ? false : true;
23953     },
23954     
23955     getGmapContext: function() 
23956     {
23957         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
23958     },
23959     
23960     GMapContext: function() 
23961     {
23962         var position = new google.maps.LatLng(this.latitude, this.longitude);
23963         
23964         var _map = new google.maps.Map(this.el.dom, {
23965             center: position,
23966             zoom: this.zoom,
23967             mapTypeId: this.mapTypeId,
23968             mapTypeControl: this.mapTypeControl,
23969             disableDoubleClickZoom: this.disableDoubleClickZoom,
23970             scrollwheel: this.scrollwheel,
23971             streetViewControl: this.streetViewControl,
23972             locationName: this.locationName,
23973             draggable: this.draggable,
23974             enableAutocomplete: this.enableAutocomplete,
23975             enableReverseGeocode: this.enableReverseGeocode
23976         });
23977         
23978         var _marker = new google.maps.Marker({
23979             position: position,
23980             map: _map,
23981             title: this.markerTitle,
23982             draggable: this.draggable
23983         });
23984         
23985         return {
23986             map: _map,
23987             marker: _marker,
23988             circle: null,
23989             location: position,
23990             radius: this.radius,
23991             locationName: this.locationName,
23992             addressComponents: {
23993                 formatted_address: null,
23994                 addressLine1: null,
23995                 addressLine2: null,
23996                 streetName: null,
23997                 streetNumber: null,
23998                 city: null,
23999                 district: null,
24000                 state: null,
24001                 stateOrProvince: null
24002             },
24003             settings: this,
24004             domContainer: this.el.dom,
24005             geodecoder: new google.maps.Geocoder()
24006         };
24007     },
24008     
24009     drawCircle: function(center, radius, options) 
24010     {
24011         if (this.gMapContext.circle != null) {
24012             this.gMapContext.circle.setMap(null);
24013         }
24014         if (radius > 0) {
24015             radius *= 1;
24016             options = Roo.apply({}, options, {
24017                 strokeColor: "#0000FF",
24018                 strokeOpacity: .35,
24019                 strokeWeight: 2,
24020                 fillColor: "#0000FF",
24021                 fillOpacity: .2
24022             });
24023             
24024             options.map = this.gMapContext.map;
24025             options.radius = radius;
24026             options.center = center;
24027             this.gMapContext.circle = new google.maps.Circle(options);
24028             return this.gMapContext.circle;
24029         }
24030         
24031         return null;
24032     },
24033     
24034     setPosition: function(location) 
24035     {
24036         this.gMapContext.location = location;
24037         this.gMapContext.marker.setPosition(location);
24038         this.gMapContext.map.panTo(location);
24039         this.drawCircle(location, this.gMapContext.radius, {});
24040         
24041         var _this = this;
24042         
24043         if (this.gMapContext.settings.enableReverseGeocode) {
24044             this.gMapContext.geodecoder.geocode({
24045                 latLng: this.gMapContext.location
24046             }, function(results, status) {
24047                 
24048                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24049                     _this.gMapContext.locationName = results[0].formatted_address;
24050                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24051                     
24052                     _this.fireEvent('positionchanged', this, location);
24053                 }
24054             });
24055             
24056             return;
24057         }
24058         
24059         this.fireEvent('positionchanged', this, location);
24060     },
24061     
24062     resize: function()
24063     {
24064         google.maps.event.trigger(this.gMapContext.map, "resize");
24065         
24066         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24067         
24068         this.fireEvent('resize', this);
24069     },
24070     
24071     setPositionByLatLng: function(latitude, longitude)
24072     {
24073         this.setPosition(new google.maps.LatLng(latitude, longitude));
24074     },
24075     
24076     getCurrentPosition: function() 
24077     {
24078         return {
24079             latitude: this.gMapContext.location.lat(),
24080             longitude: this.gMapContext.location.lng()
24081         };
24082     },
24083     
24084     getAddressName: function() 
24085     {
24086         return this.gMapContext.locationName;
24087     },
24088     
24089     getAddressComponents: function() 
24090     {
24091         return this.gMapContext.addressComponents;
24092     },
24093     
24094     address_component_from_google_geocode: function(address_components) 
24095     {
24096         var result = {};
24097         
24098         for (var i = 0; i < address_components.length; i++) {
24099             var component = address_components[i];
24100             if (component.types.indexOf("postal_code") >= 0) {
24101                 result.postalCode = component.short_name;
24102             } else if (component.types.indexOf("street_number") >= 0) {
24103                 result.streetNumber = component.short_name;
24104             } else if (component.types.indexOf("route") >= 0) {
24105                 result.streetName = component.short_name;
24106             } else if (component.types.indexOf("neighborhood") >= 0) {
24107                 result.city = component.short_name;
24108             } else if (component.types.indexOf("locality") >= 0) {
24109                 result.city = component.short_name;
24110             } else if (component.types.indexOf("sublocality") >= 0) {
24111                 result.district = component.short_name;
24112             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24113                 result.stateOrProvince = component.short_name;
24114             } else if (component.types.indexOf("country") >= 0) {
24115                 result.country = component.short_name;
24116             }
24117         }
24118         
24119         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24120         result.addressLine2 = "";
24121         return result;
24122     },
24123     
24124     setZoomLevel: function(zoom)
24125     {
24126         this.gMapContext.map.setZoom(zoom);
24127     },
24128     
24129     show: function()
24130     {
24131         if(!this.el){
24132             return;
24133         }
24134         
24135         this.el.show();
24136         
24137         this.resize();
24138         
24139         this.fireEvent('show', this);
24140     },
24141     
24142     hide: function()
24143     {
24144         if(!this.el){
24145             return;
24146         }
24147         
24148         this.el.hide();
24149         
24150         this.fireEvent('hide', this);
24151     }
24152     
24153 });
24154
24155 Roo.apply(Roo.bootstrap.LocationPicker, {
24156     
24157     OverlayView : function(map, options)
24158     {
24159         options = options || {};
24160         
24161         this.setMap(map);
24162     }
24163     
24164     
24165 });/*
24166  * - LGPL
24167  *
24168  * Alert
24169  * 
24170  */
24171
24172 /**
24173  * @class Roo.bootstrap.Alert
24174  * @extends Roo.bootstrap.Component
24175  * Bootstrap Alert class
24176  * @cfg {String} title The title of alert
24177  * @cfg {String} html The content of alert
24178  * @cfg {String} weight (  success | info | warning | danger )
24179  * @cfg {String} faicon font-awesomeicon
24180  * 
24181  * @constructor
24182  * Create a new alert
24183  * @param {Object} config The config object
24184  */
24185
24186
24187 Roo.bootstrap.Alert = function(config){
24188     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24189     
24190 };
24191
24192 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24193     
24194     title: '',
24195     html: '',
24196     weight: false,
24197     faicon: false,
24198     
24199     getAutoCreate : function()
24200     {
24201         
24202         var cfg = {
24203             tag : 'div',
24204             cls : 'alert',
24205             cn : [
24206                 {
24207                     tag : 'i',
24208                     cls : 'roo-alert-icon'
24209                     
24210                 },
24211                 {
24212                     tag : 'b',
24213                     cls : 'roo-alert-title',
24214                     html : this.title
24215                 },
24216                 {
24217                     tag : 'span',
24218                     cls : 'roo-alert-text',
24219                     html : this.html
24220                 }
24221             ]
24222         };
24223         
24224         if(this.faicon){
24225             cfg.cn[0].cls += ' fa ' + this.faicon;
24226         }
24227         
24228         if(this.weight){
24229             cfg.cls += ' alert-' + this.weight;
24230         }
24231         
24232         return cfg;
24233     },
24234     
24235     initEvents: function() 
24236     {
24237         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24238     },
24239     
24240     setTitle : function(str)
24241     {
24242         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24243     },
24244     
24245     setText : function(str)
24246     {
24247         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24248     },
24249     
24250     setWeight : function(weight)
24251     {
24252         if(this.weight){
24253             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24254         }
24255         
24256         this.weight = weight;
24257         
24258         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24259     },
24260     
24261     setIcon : function(icon)
24262     {
24263         if(this.faicon){
24264             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24265         }
24266         
24267         this.faicon = icon;
24268         
24269         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24270     },
24271     
24272     hide: function() 
24273     {
24274         this.el.hide();   
24275     },
24276     
24277     show: function() 
24278     {  
24279         this.el.show();   
24280     }
24281     
24282 });
24283
24284  
24285 /*
24286 * Licence: LGPL
24287 */
24288
24289 /**
24290  * @class Roo.bootstrap.UploadCropbox
24291  * @extends Roo.bootstrap.Component
24292  * Bootstrap UploadCropbox class
24293  * @cfg {String} emptyText show when image has been loaded
24294  * @cfg {String} rotateNotify show when image too small to rotate
24295  * @cfg {Number} errorTimeout default 3000
24296  * @cfg {Number} minWidth default 300
24297  * @cfg {Number} minHeight default 300
24298  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24299  * @cfg {Boolean} isDocument (true|false) default false
24300  * @cfg {String} url action url
24301  * @cfg {String} paramName default 'imageUpload'
24302  * @cfg {String} method default POST
24303  * @cfg {Boolean} loadMask (true|false) default true
24304  * @cfg {Boolean} loadingText default 'Loading...'
24305  * 
24306  * @constructor
24307  * Create a new UploadCropbox
24308  * @param {Object} config The config object
24309  */
24310
24311 Roo.bootstrap.UploadCropbox = function(config){
24312     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24313     
24314     this.addEvents({
24315         /**
24316          * @event beforeselectfile
24317          * Fire before select file
24318          * @param {Roo.bootstrap.UploadCropbox} this
24319          */
24320         "beforeselectfile" : true,
24321         /**
24322          * @event initial
24323          * Fire after initEvent
24324          * @param {Roo.bootstrap.UploadCropbox} this
24325          */
24326         "initial" : true,
24327         /**
24328          * @event crop
24329          * Fire after initEvent
24330          * @param {Roo.bootstrap.UploadCropbox} this
24331          * @param {String} data
24332          */
24333         "crop" : true,
24334         /**
24335          * @event prepare
24336          * Fire when preparing the file data
24337          * @param {Roo.bootstrap.UploadCropbox} this
24338          * @param {Object} file
24339          */
24340         "prepare" : true,
24341         /**
24342          * @event exception
24343          * Fire when get exception
24344          * @param {Roo.bootstrap.UploadCropbox} this
24345          * @param {XMLHttpRequest} xhr
24346          */
24347         "exception" : true,
24348         /**
24349          * @event beforeloadcanvas
24350          * Fire before load the canvas
24351          * @param {Roo.bootstrap.UploadCropbox} this
24352          * @param {String} src
24353          */
24354         "beforeloadcanvas" : true,
24355         /**
24356          * @event trash
24357          * Fire when trash image
24358          * @param {Roo.bootstrap.UploadCropbox} this
24359          */
24360         "trash" : true,
24361         /**
24362          * @event download
24363          * Fire when download the image
24364          * @param {Roo.bootstrap.UploadCropbox} this
24365          */
24366         "download" : true,
24367         /**
24368          * @event footerbuttonclick
24369          * Fire when footerbuttonclick
24370          * @param {Roo.bootstrap.UploadCropbox} this
24371          * @param {String} type
24372          */
24373         "footerbuttonclick" : true,
24374         /**
24375          * @event resize
24376          * Fire when resize
24377          * @param {Roo.bootstrap.UploadCropbox} this
24378          */
24379         "resize" : true,
24380         /**
24381          * @event rotate
24382          * Fire when rotate the image
24383          * @param {Roo.bootstrap.UploadCropbox} this
24384          * @param {String} pos
24385          */
24386         "rotate" : true,
24387         /**
24388          * @event inspect
24389          * Fire when inspect the file
24390          * @param {Roo.bootstrap.UploadCropbox} this
24391          * @param {Object} file
24392          */
24393         "inspect" : true,
24394         /**
24395          * @event upload
24396          * Fire when xhr upload the file
24397          * @param {Roo.bootstrap.UploadCropbox} this
24398          * @param {Object} data
24399          */
24400         "upload" : true,
24401         /**
24402          * @event arrange
24403          * Fire when arrange the file data
24404          * @param {Roo.bootstrap.UploadCropbox} this
24405          * @param {Object} formData
24406          */
24407         "arrange" : true
24408     });
24409     
24410     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24411 };
24412
24413 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24414     
24415     emptyText : 'Click to upload image',
24416     rotateNotify : 'Image is too small to rotate',
24417     errorTimeout : 3000,
24418     scale : 0,
24419     baseScale : 1,
24420     rotate : 0,
24421     dragable : false,
24422     pinching : false,
24423     mouseX : 0,
24424     mouseY : 0,
24425     cropData : false,
24426     minWidth : 300,
24427     minHeight : 300,
24428     file : false,
24429     exif : {},
24430     baseRotate : 1,
24431     cropType : 'image/jpeg',
24432     buttons : false,
24433     canvasLoaded : false,
24434     isDocument : false,
24435     method : 'POST',
24436     paramName : 'imageUpload',
24437     loadMask : true,
24438     loadingText : 'Loading...',
24439     maskEl : false,
24440     
24441     getAutoCreate : function()
24442     {
24443         var cfg = {
24444             tag : 'div',
24445             cls : 'roo-upload-cropbox',
24446             cn : [
24447                 {
24448                     tag : 'input',
24449                     cls : 'roo-upload-cropbox-selector',
24450                     type : 'file'
24451                 },
24452                 {
24453                     tag : 'div',
24454                     cls : 'roo-upload-cropbox-body',
24455                     style : 'cursor:pointer',
24456                     cn : [
24457                         {
24458                             tag : 'div',
24459                             cls : 'roo-upload-cropbox-preview'
24460                         },
24461                         {
24462                             tag : 'div',
24463                             cls : 'roo-upload-cropbox-thumb'
24464                         },
24465                         {
24466                             tag : 'div',
24467                             cls : 'roo-upload-cropbox-empty-notify',
24468                             html : this.emptyText
24469                         },
24470                         {
24471                             tag : 'div',
24472                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24473                             html : this.rotateNotify
24474                         }
24475                     ]
24476                 },
24477                 {
24478                     tag : 'div',
24479                     cls : 'roo-upload-cropbox-footer',
24480                     cn : {
24481                         tag : 'div',
24482                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24483                         cn : []
24484                     }
24485                 }
24486             ]
24487         };
24488         
24489         return cfg;
24490     },
24491     
24492     onRender : function(ct, position)
24493     {
24494         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24495         
24496         if (this.buttons.length) {
24497             
24498             Roo.each(this.buttons, function(bb) {
24499                 
24500                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24501                 
24502                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24503                 
24504             }, this);
24505         }
24506         
24507         if(this.loadMask){
24508             this.maskEl = this.el;
24509         }
24510     },
24511     
24512     initEvents : function()
24513     {
24514         this.urlAPI = (window.createObjectURL && window) || 
24515                                 (window.URL && URL.revokeObjectURL && URL) || 
24516                                 (window.webkitURL && webkitURL);
24517                         
24518         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24519         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24520         
24521         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24522         this.selectorEl.hide();
24523         
24524         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24525         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24526         
24527         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24528         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24529         this.thumbEl.hide();
24530         
24531         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24532         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24533         
24534         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24535         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24536         this.errorEl.hide();
24537         
24538         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24539         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24540         this.footerEl.hide();
24541         
24542         this.setThumbBoxSize();
24543         
24544         this.bind();
24545         
24546         this.resize();
24547         
24548         this.fireEvent('initial', this);
24549     },
24550
24551     bind : function()
24552     {
24553         var _this = this;
24554         
24555         window.addEventListener("resize", function() { _this.resize(); } );
24556         
24557         this.bodyEl.on('click', this.beforeSelectFile, this);
24558         
24559         if(Roo.isTouch){
24560             this.bodyEl.on('touchstart', this.onTouchStart, this);
24561             this.bodyEl.on('touchmove', this.onTouchMove, this);
24562             this.bodyEl.on('touchend', this.onTouchEnd, this);
24563         }
24564         
24565         if(!Roo.isTouch){
24566             this.bodyEl.on('mousedown', this.onMouseDown, this);
24567             this.bodyEl.on('mousemove', this.onMouseMove, this);
24568             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24569             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24570             Roo.get(document).on('mouseup', this.onMouseUp, this);
24571         }
24572         
24573         this.selectorEl.on('change', this.onFileSelected, this);
24574     },
24575     
24576     reset : function()
24577     {    
24578         this.scale = 0;
24579         this.baseScale = 1;
24580         this.rotate = 0;
24581         this.baseRotate = 1;
24582         this.dragable = false;
24583         this.pinching = false;
24584         this.mouseX = 0;
24585         this.mouseY = 0;
24586         this.cropData = false;
24587         this.notifyEl.dom.innerHTML = this.emptyText;
24588         
24589         this.selectorEl.dom.value = '';
24590         
24591     },
24592     
24593     resize : function()
24594     {
24595         if(this.fireEvent('resize', this) != false){
24596             this.setThumbBoxPosition();
24597             this.setCanvasPosition();
24598         }
24599     },
24600     
24601     onFooterButtonClick : function(e, el, o, type)
24602     {
24603         switch (type) {
24604             case 'rotate-left' :
24605                 this.onRotateLeft(e);
24606                 break;
24607             case 'rotate-right' :
24608                 this.onRotateRight(e);
24609                 break;
24610             case 'picture' :
24611                 this.beforeSelectFile(e);
24612                 break;
24613             case 'trash' :
24614                 this.trash(e);
24615                 break;
24616             case 'crop' :
24617                 this.crop(e);
24618                 break;
24619             case 'download' :
24620                 this.download(e);
24621                 break;
24622             default :
24623                 break;
24624         }
24625         
24626         this.fireEvent('footerbuttonclick', this, type);
24627     },
24628     
24629     beforeSelectFile : function(e)
24630     {
24631         e.preventDefault();
24632         
24633         if(this.fireEvent('beforeselectfile', this) != false){
24634             this.selectorEl.dom.click();
24635         }
24636     },
24637     
24638     onFileSelected : function(e)
24639     {
24640         e.preventDefault();
24641         
24642         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24643             return;
24644         }
24645         
24646         var file = this.selectorEl.dom.files[0];
24647         
24648         if(this.fireEvent('inspect', this, file) != false){
24649             this.prepare(file);
24650         }
24651         
24652     },
24653     
24654     trash : function(e)
24655     {
24656         this.fireEvent('trash', this);
24657     },
24658     
24659     download : function(e)
24660     {
24661         this.fireEvent('download', this);
24662     },
24663     
24664     loadCanvas : function(src)
24665     {   
24666         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24667             
24668             this.reset();
24669             
24670             this.imageEl = document.createElement('img');
24671             
24672             var _this = this;
24673             
24674             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24675             
24676             this.imageEl.src = src;
24677         }
24678     },
24679     
24680     onLoadCanvas : function()
24681     {   
24682         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24683         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24684         
24685         this.bodyEl.un('click', this.beforeSelectFile, this);
24686         
24687         this.notifyEl.hide();
24688         this.thumbEl.show();
24689         this.footerEl.show();
24690         
24691         this.baseRotateLevel();
24692         
24693         if(this.isDocument){
24694             this.setThumbBoxSize();
24695         }
24696         
24697         this.setThumbBoxPosition();
24698         
24699         this.baseScaleLevel();
24700         
24701         this.draw();
24702         
24703         this.resize();
24704         
24705         this.canvasLoaded = true;
24706         
24707         if(this.loadMask){
24708             this.maskEl.unmask();
24709         }
24710         
24711     },
24712     
24713     setCanvasPosition : function()
24714     {   
24715         if(!this.canvasEl){
24716             return;
24717         }
24718         
24719         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24720         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24721         
24722         this.previewEl.setLeft(pw);
24723         this.previewEl.setTop(ph);
24724         
24725     },
24726     
24727     onMouseDown : function(e)
24728     {   
24729         e.stopEvent();
24730         
24731         this.dragable = true;
24732         this.pinching = false;
24733         
24734         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24735             this.dragable = false;
24736             return;
24737         }
24738         
24739         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24740         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24741         
24742     },
24743     
24744     onMouseMove : function(e)
24745     {   
24746         e.stopEvent();
24747         
24748         if(!this.canvasLoaded){
24749             return;
24750         }
24751         
24752         if (!this.dragable){
24753             return;
24754         }
24755         
24756         var minX = Math.ceil(this.thumbEl.getLeft(true));
24757         var minY = Math.ceil(this.thumbEl.getTop(true));
24758         
24759         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24760         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24761         
24762         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24763         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24764         
24765         x = x - this.mouseX;
24766         y = y - this.mouseY;
24767         
24768         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24769         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24770         
24771         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24772         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24773         
24774         this.previewEl.setLeft(bgX);
24775         this.previewEl.setTop(bgY);
24776         
24777         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24778         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24779     },
24780     
24781     onMouseUp : function(e)
24782     {   
24783         e.stopEvent();
24784         
24785         this.dragable = false;
24786     },
24787     
24788     onMouseWheel : function(e)
24789     {   
24790         e.stopEvent();
24791         
24792         this.startScale = this.scale;
24793         
24794         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24795         
24796         if(!this.zoomable()){
24797             this.scale = this.startScale;
24798             return;
24799         }
24800         
24801         this.draw();
24802         
24803         return;
24804     },
24805     
24806     zoomable : function()
24807     {
24808         var minScale = this.thumbEl.getWidth() / this.minWidth;
24809         
24810         if(this.minWidth < this.minHeight){
24811             minScale = this.thumbEl.getHeight() / this.minHeight;
24812         }
24813         
24814         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24815         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24816         
24817         if(
24818                 this.isDocument &&
24819                 (this.rotate == 0 || this.rotate == 180) && 
24820                 (
24821                     width > this.imageEl.OriginWidth || 
24822                     height > this.imageEl.OriginHeight ||
24823                     (width < this.minWidth && height < this.minHeight)
24824                 )
24825         ){
24826             return false;
24827         }
24828         
24829         if(
24830                 this.isDocument &&
24831                 (this.rotate == 90 || this.rotate == 270) && 
24832                 (
24833                     width > this.imageEl.OriginWidth || 
24834                     height > this.imageEl.OriginHeight ||
24835                     (width < this.minHeight && height < this.minWidth)
24836                 )
24837         ){
24838             return false;
24839         }
24840         
24841         if(
24842                 !this.isDocument &&
24843                 (this.rotate == 0 || this.rotate == 180) && 
24844                 (
24845                     width < this.minWidth || 
24846                     width > this.imageEl.OriginWidth || 
24847                     height < this.minHeight || 
24848                     height > this.imageEl.OriginHeight
24849                 )
24850         ){
24851             return false;
24852         }
24853         
24854         if(
24855                 !this.isDocument &&
24856                 (this.rotate == 90 || this.rotate == 270) && 
24857                 (
24858                     width < this.minHeight || 
24859                     width > this.imageEl.OriginWidth || 
24860                     height < this.minWidth || 
24861                     height > this.imageEl.OriginHeight
24862                 )
24863         ){
24864             return false;
24865         }
24866         
24867         return true;
24868         
24869     },
24870     
24871     onRotateLeft : function(e)
24872     {   
24873         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24874             
24875             var minScale = this.thumbEl.getWidth() / this.minWidth;
24876             
24877             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24878             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24879             
24880             this.startScale = this.scale;
24881             
24882             while (this.getScaleLevel() < minScale){
24883             
24884                 this.scale = this.scale + 1;
24885                 
24886                 if(!this.zoomable()){
24887                     break;
24888                 }
24889                 
24890                 if(
24891                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24892                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24893                 ){
24894                     continue;
24895                 }
24896                 
24897                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24898
24899                 this.draw();
24900                 
24901                 return;
24902             }
24903             
24904             this.scale = this.startScale;
24905             
24906             this.onRotateFail();
24907             
24908             return false;
24909         }
24910         
24911         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24912
24913         if(this.isDocument){
24914             this.setThumbBoxSize();
24915             this.setThumbBoxPosition();
24916             this.setCanvasPosition();
24917         }
24918         
24919         this.draw();
24920         
24921         this.fireEvent('rotate', this, 'left');
24922         
24923     },
24924     
24925     onRotateRight : function(e)
24926     {
24927         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24928             
24929             var minScale = this.thumbEl.getWidth() / this.minWidth;
24930         
24931             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24932             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24933             
24934             this.startScale = this.scale;
24935             
24936             while (this.getScaleLevel() < minScale){
24937             
24938                 this.scale = this.scale + 1;
24939                 
24940                 if(!this.zoomable()){
24941                     break;
24942                 }
24943                 
24944                 if(
24945                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24946                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24947                 ){
24948                     continue;
24949                 }
24950                 
24951                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24952
24953                 this.draw();
24954                 
24955                 return;
24956             }
24957             
24958             this.scale = this.startScale;
24959             
24960             this.onRotateFail();
24961             
24962             return false;
24963         }
24964         
24965         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24966
24967         if(this.isDocument){
24968             this.setThumbBoxSize();
24969             this.setThumbBoxPosition();
24970             this.setCanvasPosition();
24971         }
24972         
24973         this.draw();
24974         
24975         this.fireEvent('rotate', this, 'right');
24976     },
24977     
24978     onRotateFail : function()
24979     {
24980         this.errorEl.show(true);
24981         
24982         var _this = this;
24983         
24984         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24985     },
24986     
24987     draw : function()
24988     {
24989         this.previewEl.dom.innerHTML = '';
24990         
24991         var canvasEl = document.createElement("canvas");
24992         
24993         var contextEl = canvasEl.getContext("2d");
24994         
24995         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24996         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24997         var center = this.imageEl.OriginWidth / 2;
24998         
24999         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25000             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25001             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25002             center = this.imageEl.OriginHeight / 2;
25003         }
25004         
25005         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25006         
25007         contextEl.translate(center, center);
25008         contextEl.rotate(this.rotate * Math.PI / 180);
25009
25010         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25011         
25012         this.canvasEl = document.createElement("canvas");
25013         
25014         this.contextEl = this.canvasEl.getContext("2d");
25015         
25016         switch (this.rotate) {
25017             case 0 :
25018                 
25019                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25020                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25021                 
25022                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25023                 
25024                 break;
25025             case 90 : 
25026                 
25027                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25028                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25029                 
25030                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25031                     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);
25032                     break;
25033                 }
25034                 
25035                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25036                 
25037                 break;
25038             case 180 :
25039                 
25040                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25041                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25042                 
25043                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25044                     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);
25045                     break;
25046                 }
25047                 
25048                 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);
25049                 
25050                 break;
25051             case 270 :
25052                 
25053                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25054                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25055         
25056                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25057                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25058                     break;
25059                 }
25060                 
25061                 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);
25062                 
25063                 break;
25064             default : 
25065                 break;
25066         }
25067         
25068         this.previewEl.appendChild(this.canvasEl);
25069         
25070         this.setCanvasPosition();
25071     },
25072     
25073     crop : function()
25074     {
25075         if(!this.canvasLoaded){
25076             return;
25077         }
25078         
25079         var imageCanvas = document.createElement("canvas");
25080         
25081         var imageContext = imageCanvas.getContext("2d");
25082         
25083         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25084         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25085         
25086         var center = imageCanvas.width / 2;
25087         
25088         imageContext.translate(center, center);
25089         
25090         imageContext.rotate(this.rotate * Math.PI / 180);
25091         
25092         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25093         
25094         var canvas = document.createElement("canvas");
25095         
25096         var context = canvas.getContext("2d");
25097                 
25098         canvas.width = this.minWidth;
25099         canvas.height = this.minHeight;
25100
25101         switch (this.rotate) {
25102             case 0 :
25103                 
25104                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25105                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25106                 
25107                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25108                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25109                 
25110                 var targetWidth = this.minWidth - 2 * x;
25111                 var targetHeight = this.minHeight - 2 * y;
25112                 
25113                 var scale = 1;
25114                 
25115                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25116                     scale = targetWidth / width;
25117                 }
25118                 
25119                 if(x > 0 && y == 0){
25120                     scale = targetHeight / height;
25121                 }
25122                 
25123                 if(x > 0 && y > 0){
25124                     scale = targetWidth / width;
25125                     
25126                     if(width < height){
25127                         scale = targetHeight / height;
25128                     }
25129                 }
25130                 
25131                 context.scale(scale, scale);
25132                 
25133                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25134                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25135
25136                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25137                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25138
25139                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25140                 
25141                 break;
25142             case 90 : 
25143                 
25144                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25145                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25146                 
25147                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25148                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25149                 
25150                 var targetWidth = this.minWidth - 2 * x;
25151                 var targetHeight = this.minHeight - 2 * y;
25152                 
25153                 var scale = 1;
25154                 
25155                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25156                     scale = targetWidth / width;
25157                 }
25158                 
25159                 if(x > 0 && y == 0){
25160                     scale = targetHeight / height;
25161                 }
25162                 
25163                 if(x > 0 && y > 0){
25164                     scale = targetWidth / width;
25165                     
25166                     if(width < height){
25167                         scale = targetHeight / height;
25168                     }
25169                 }
25170                 
25171                 context.scale(scale, scale);
25172                 
25173                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25174                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25175
25176                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25177                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25178                 
25179                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25180                 
25181                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25182                 
25183                 break;
25184             case 180 :
25185                 
25186                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25187                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25188                 
25189                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25190                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25191                 
25192                 var targetWidth = this.minWidth - 2 * x;
25193                 var targetHeight = this.minHeight - 2 * y;
25194                 
25195                 var scale = 1;
25196                 
25197                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25198                     scale = targetWidth / width;
25199                 }
25200                 
25201                 if(x > 0 && y == 0){
25202                     scale = targetHeight / height;
25203                 }
25204                 
25205                 if(x > 0 && y > 0){
25206                     scale = targetWidth / width;
25207                     
25208                     if(width < height){
25209                         scale = targetHeight / height;
25210                     }
25211                 }
25212                 
25213                 context.scale(scale, scale);
25214                 
25215                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25216                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25217
25218                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25219                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25220
25221                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25222                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25223                 
25224                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25225                 
25226                 break;
25227             case 270 :
25228                 
25229                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25230                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25231                 
25232                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25233                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25234                 
25235                 var targetWidth = this.minWidth - 2 * x;
25236                 var targetHeight = this.minHeight - 2 * y;
25237                 
25238                 var scale = 1;
25239                 
25240                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25241                     scale = targetWidth / width;
25242                 }
25243                 
25244                 if(x > 0 && y == 0){
25245                     scale = targetHeight / height;
25246                 }
25247                 
25248                 if(x > 0 && y > 0){
25249                     scale = targetWidth / width;
25250                     
25251                     if(width < height){
25252                         scale = targetHeight / height;
25253                     }
25254                 }
25255                 
25256                 context.scale(scale, scale);
25257                 
25258                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25259                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25260
25261                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25262                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25263                 
25264                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25265                 
25266                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25267                 
25268                 break;
25269             default : 
25270                 break;
25271         }
25272         
25273         this.cropData = canvas.toDataURL(this.cropType);
25274         
25275         if(this.fireEvent('crop', this, this.cropData) !== false){
25276             this.process(this.file, this.cropData);
25277         }
25278         
25279         return;
25280         
25281     },
25282     
25283     setThumbBoxSize : function()
25284     {
25285         var width, height;
25286         
25287         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25288             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25289             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25290             
25291             this.minWidth = width;
25292             this.minHeight = height;
25293             
25294             if(this.rotate == 90 || this.rotate == 270){
25295                 this.minWidth = height;
25296                 this.minHeight = width;
25297             }
25298         }
25299         
25300         height = 300;
25301         width = Math.ceil(this.minWidth * height / this.minHeight);
25302         
25303         if(this.minWidth > this.minHeight){
25304             width = 300;
25305             height = Math.ceil(this.minHeight * width / this.minWidth);
25306         }
25307         
25308         this.thumbEl.setStyle({
25309             width : width + 'px',
25310             height : height + 'px'
25311         });
25312
25313         return;
25314             
25315     },
25316     
25317     setThumbBoxPosition : function()
25318     {
25319         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25320         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25321         
25322         this.thumbEl.setLeft(x);
25323         this.thumbEl.setTop(y);
25324         
25325     },
25326     
25327     baseRotateLevel : function()
25328     {
25329         this.baseRotate = 1;
25330         
25331         if(
25332                 typeof(this.exif) != 'undefined' &&
25333                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25334                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25335         ){
25336             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25337         }
25338         
25339         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25340         
25341     },
25342     
25343     baseScaleLevel : function()
25344     {
25345         var width, height;
25346         
25347         if(this.isDocument){
25348             
25349             if(this.baseRotate == 6 || this.baseRotate == 8){
25350             
25351                 height = this.thumbEl.getHeight();
25352                 this.baseScale = height / this.imageEl.OriginWidth;
25353
25354                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25355                     width = this.thumbEl.getWidth();
25356                     this.baseScale = width / this.imageEl.OriginHeight;
25357                 }
25358
25359                 return;
25360             }
25361
25362             height = this.thumbEl.getHeight();
25363             this.baseScale = height / this.imageEl.OriginHeight;
25364
25365             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25366                 width = this.thumbEl.getWidth();
25367                 this.baseScale = width / this.imageEl.OriginWidth;
25368             }
25369
25370             return;
25371         }
25372         
25373         if(this.baseRotate == 6 || this.baseRotate == 8){
25374             
25375             width = this.thumbEl.getHeight();
25376             this.baseScale = width / this.imageEl.OriginHeight;
25377             
25378             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25379                 height = this.thumbEl.getWidth();
25380                 this.baseScale = height / this.imageEl.OriginHeight;
25381             }
25382             
25383             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25384                 height = this.thumbEl.getWidth();
25385                 this.baseScale = height / this.imageEl.OriginHeight;
25386                 
25387                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25388                     width = this.thumbEl.getHeight();
25389                     this.baseScale = width / this.imageEl.OriginWidth;
25390                 }
25391             }
25392             
25393             return;
25394         }
25395         
25396         width = this.thumbEl.getWidth();
25397         this.baseScale = width / this.imageEl.OriginWidth;
25398         
25399         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25400             height = this.thumbEl.getHeight();
25401             this.baseScale = height / this.imageEl.OriginHeight;
25402         }
25403         
25404         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25405             
25406             height = this.thumbEl.getHeight();
25407             this.baseScale = height / this.imageEl.OriginHeight;
25408             
25409             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25410                 width = this.thumbEl.getWidth();
25411                 this.baseScale = width / this.imageEl.OriginWidth;
25412             }
25413             
25414         }
25415         
25416         return;
25417     },
25418     
25419     getScaleLevel : function()
25420     {
25421         return this.baseScale * Math.pow(1.1, this.scale);
25422     },
25423     
25424     onTouchStart : function(e)
25425     {
25426         if(!this.canvasLoaded){
25427             this.beforeSelectFile(e);
25428             return;
25429         }
25430         
25431         var touches = e.browserEvent.touches;
25432         
25433         if(!touches){
25434             return;
25435         }
25436         
25437         if(touches.length == 1){
25438             this.onMouseDown(e);
25439             return;
25440         }
25441         
25442         if(touches.length != 2){
25443             return;
25444         }
25445         
25446         var coords = [];
25447         
25448         for(var i = 0, finger; finger = touches[i]; i++){
25449             coords.push(finger.pageX, finger.pageY);
25450         }
25451         
25452         var x = Math.pow(coords[0] - coords[2], 2);
25453         var y = Math.pow(coords[1] - coords[3], 2);
25454         
25455         this.startDistance = Math.sqrt(x + y);
25456         
25457         this.startScale = this.scale;
25458         
25459         this.pinching = true;
25460         this.dragable = false;
25461         
25462     },
25463     
25464     onTouchMove : function(e)
25465     {
25466         if(!this.pinching && !this.dragable){
25467             return;
25468         }
25469         
25470         var touches = e.browserEvent.touches;
25471         
25472         if(!touches){
25473             return;
25474         }
25475         
25476         if(this.dragable){
25477             this.onMouseMove(e);
25478             return;
25479         }
25480         
25481         var coords = [];
25482         
25483         for(var i = 0, finger; finger = touches[i]; i++){
25484             coords.push(finger.pageX, finger.pageY);
25485         }
25486         
25487         var x = Math.pow(coords[0] - coords[2], 2);
25488         var y = Math.pow(coords[1] - coords[3], 2);
25489         
25490         this.endDistance = Math.sqrt(x + y);
25491         
25492         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25493         
25494         if(!this.zoomable()){
25495             this.scale = this.startScale;
25496             return;
25497         }
25498         
25499         this.draw();
25500         
25501     },
25502     
25503     onTouchEnd : function(e)
25504     {
25505         this.pinching = false;
25506         this.dragable = false;
25507         
25508     },
25509     
25510     process : function(file, crop)
25511     {
25512         if(this.loadMask){
25513             this.maskEl.mask(this.loadingText);
25514         }
25515         
25516         this.xhr = new XMLHttpRequest();
25517         
25518         file.xhr = this.xhr;
25519
25520         this.xhr.open(this.method, this.url, true);
25521         
25522         var headers = {
25523             "Accept": "application/json",
25524             "Cache-Control": "no-cache",
25525             "X-Requested-With": "XMLHttpRequest"
25526         };
25527         
25528         for (var headerName in headers) {
25529             var headerValue = headers[headerName];
25530             if (headerValue) {
25531                 this.xhr.setRequestHeader(headerName, headerValue);
25532             }
25533         }
25534         
25535         var _this = this;
25536         
25537         this.xhr.onload = function()
25538         {
25539             _this.xhrOnLoad(_this.xhr);
25540         }
25541         
25542         this.xhr.onerror = function()
25543         {
25544             _this.xhrOnError(_this.xhr);
25545         }
25546         
25547         var formData = new FormData();
25548
25549         formData.append('returnHTML', 'NO');
25550         
25551         if(crop){
25552             formData.append('crop', crop);
25553         }
25554         
25555         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25556             formData.append(this.paramName, file, file.name);
25557         }
25558         
25559         if(typeof(file.filename) != 'undefined'){
25560             formData.append('filename', file.filename);
25561         }
25562         
25563         if(typeof(file.mimetype) != 'undefined'){
25564             formData.append('mimetype', file.mimetype);
25565         }
25566         
25567         if(this.fireEvent('arrange', this, formData) != false){
25568             this.xhr.send(formData);
25569         };
25570     },
25571     
25572     xhrOnLoad : function(xhr)
25573     {
25574         if(this.loadMask){
25575             this.maskEl.unmask();
25576         }
25577         
25578         if (xhr.readyState !== 4) {
25579             this.fireEvent('exception', this, xhr);
25580             return;
25581         }
25582
25583         var response = Roo.decode(xhr.responseText);
25584         
25585         if(!response.success){
25586             this.fireEvent('exception', this, xhr);
25587             return;
25588         }
25589         
25590         var response = Roo.decode(xhr.responseText);
25591         
25592         this.fireEvent('upload', this, response);
25593         
25594     },
25595     
25596     xhrOnError : function()
25597     {
25598         if(this.loadMask){
25599             this.maskEl.unmask();
25600         }
25601         
25602         Roo.log('xhr on error');
25603         
25604         var response = Roo.decode(xhr.responseText);
25605           
25606         Roo.log(response);
25607         
25608     },
25609     
25610     prepare : function(file)
25611     {   
25612         if(this.loadMask){
25613             this.maskEl.mask(this.loadingText);
25614         }
25615         
25616         this.file = false;
25617         this.exif = {};
25618         
25619         if(typeof(file) === 'string'){
25620             this.loadCanvas(file);
25621             return;
25622         }
25623         
25624         if(!file || !this.urlAPI){
25625             return;
25626         }
25627         
25628         this.file = file;
25629         this.cropType = file.type;
25630         
25631         var _this = this;
25632         
25633         if(this.fireEvent('prepare', this, this.file) != false){
25634             
25635             var reader = new FileReader();
25636             
25637             reader.onload = function (e) {
25638                 if (e.target.error) {
25639                     Roo.log(e.target.error);
25640                     return;
25641                 }
25642                 
25643                 var buffer = e.target.result,
25644                     dataView = new DataView(buffer),
25645                     offset = 2,
25646                     maxOffset = dataView.byteLength - 4,
25647                     markerBytes,
25648                     markerLength;
25649                 
25650                 if (dataView.getUint16(0) === 0xffd8) {
25651                     while (offset < maxOffset) {
25652                         markerBytes = dataView.getUint16(offset);
25653                         
25654                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25655                             markerLength = dataView.getUint16(offset + 2) + 2;
25656                             if (offset + markerLength > dataView.byteLength) {
25657                                 Roo.log('Invalid meta data: Invalid segment size.');
25658                                 break;
25659                             }
25660                             
25661                             if(markerBytes == 0xffe1){
25662                                 _this.parseExifData(
25663                                     dataView,
25664                                     offset,
25665                                     markerLength
25666                                 );
25667                             }
25668                             
25669                             offset += markerLength;
25670                             
25671                             continue;
25672                         }
25673                         
25674                         break;
25675                     }
25676                     
25677                 }
25678                 
25679                 var url = _this.urlAPI.createObjectURL(_this.file);
25680                 
25681                 _this.loadCanvas(url);
25682                 
25683                 return;
25684             }
25685             
25686             reader.readAsArrayBuffer(this.file);
25687             
25688         }
25689         
25690     },
25691     
25692     parseExifData : function(dataView, offset, length)
25693     {
25694         var tiffOffset = offset + 10,
25695             littleEndian,
25696             dirOffset;
25697     
25698         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25699             // No Exif data, might be XMP data instead
25700             return;
25701         }
25702         
25703         // Check for the ASCII code for "Exif" (0x45786966):
25704         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25705             // No Exif data, might be XMP data instead
25706             return;
25707         }
25708         if (tiffOffset + 8 > dataView.byteLength) {
25709             Roo.log('Invalid Exif data: Invalid segment size.');
25710             return;
25711         }
25712         // Check for the two null bytes:
25713         if (dataView.getUint16(offset + 8) !== 0x0000) {
25714             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25715             return;
25716         }
25717         // Check the byte alignment:
25718         switch (dataView.getUint16(tiffOffset)) {
25719         case 0x4949:
25720             littleEndian = true;
25721             break;
25722         case 0x4D4D:
25723             littleEndian = false;
25724             break;
25725         default:
25726             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25727             return;
25728         }
25729         // Check for the TIFF tag marker (0x002A):
25730         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25731             Roo.log('Invalid Exif data: Missing TIFF marker.');
25732             return;
25733         }
25734         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25735         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25736         
25737         this.parseExifTags(
25738             dataView,
25739             tiffOffset,
25740             tiffOffset + dirOffset,
25741             littleEndian
25742         );
25743     },
25744     
25745     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25746     {
25747         var tagsNumber,
25748             dirEndOffset,
25749             i;
25750         if (dirOffset + 6 > dataView.byteLength) {
25751             Roo.log('Invalid Exif data: Invalid directory offset.');
25752             return;
25753         }
25754         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25755         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25756         if (dirEndOffset + 4 > dataView.byteLength) {
25757             Roo.log('Invalid Exif data: Invalid directory size.');
25758             return;
25759         }
25760         for (i = 0; i < tagsNumber; i += 1) {
25761             this.parseExifTag(
25762                 dataView,
25763                 tiffOffset,
25764                 dirOffset + 2 + 12 * i, // tag offset
25765                 littleEndian
25766             );
25767         }
25768         // Return the offset to the next directory:
25769         return dataView.getUint32(dirEndOffset, littleEndian);
25770     },
25771     
25772     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25773     {
25774         var tag = dataView.getUint16(offset, littleEndian);
25775         
25776         this.exif[tag] = this.getExifValue(
25777             dataView,
25778             tiffOffset,
25779             offset,
25780             dataView.getUint16(offset + 2, littleEndian), // tag type
25781             dataView.getUint32(offset + 4, littleEndian), // tag length
25782             littleEndian
25783         );
25784     },
25785     
25786     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25787     {
25788         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25789             tagSize,
25790             dataOffset,
25791             values,
25792             i,
25793             str,
25794             c;
25795     
25796         if (!tagType) {
25797             Roo.log('Invalid Exif data: Invalid tag type.');
25798             return;
25799         }
25800         
25801         tagSize = tagType.size * length;
25802         // Determine if the value is contained in the dataOffset bytes,
25803         // or if the value at the dataOffset is a pointer to the actual data:
25804         dataOffset = tagSize > 4 ?
25805                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25806         if (dataOffset + tagSize > dataView.byteLength) {
25807             Roo.log('Invalid Exif data: Invalid data offset.');
25808             return;
25809         }
25810         if (length === 1) {
25811             return tagType.getValue(dataView, dataOffset, littleEndian);
25812         }
25813         values = [];
25814         for (i = 0; i < length; i += 1) {
25815             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25816         }
25817         
25818         if (tagType.ascii) {
25819             str = '';
25820             // Concatenate the chars:
25821             for (i = 0; i < values.length; i += 1) {
25822                 c = values[i];
25823                 // Ignore the terminating NULL byte(s):
25824                 if (c === '\u0000') {
25825                     break;
25826                 }
25827                 str += c;
25828             }
25829             return str;
25830         }
25831         return values;
25832     }
25833     
25834 });
25835
25836 Roo.apply(Roo.bootstrap.UploadCropbox, {
25837     tags : {
25838         'Orientation': 0x0112
25839     },
25840     
25841     Orientation: {
25842             1: 0, //'top-left',
25843 //            2: 'top-right',
25844             3: 180, //'bottom-right',
25845 //            4: 'bottom-left',
25846 //            5: 'left-top',
25847             6: 90, //'right-top',
25848 //            7: 'right-bottom',
25849             8: 270 //'left-bottom'
25850     },
25851     
25852     exifTagTypes : {
25853         // byte, 8-bit unsigned int:
25854         1: {
25855             getValue: function (dataView, dataOffset) {
25856                 return dataView.getUint8(dataOffset);
25857             },
25858             size: 1
25859         },
25860         // ascii, 8-bit byte:
25861         2: {
25862             getValue: function (dataView, dataOffset) {
25863                 return String.fromCharCode(dataView.getUint8(dataOffset));
25864             },
25865             size: 1,
25866             ascii: true
25867         },
25868         // short, 16 bit int:
25869         3: {
25870             getValue: function (dataView, dataOffset, littleEndian) {
25871                 return dataView.getUint16(dataOffset, littleEndian);
25872             },
25873             size: 2
25874         },
25875         // long, 32 bit int:
25876         4: {
25877             getValue: function (dataView, dataOffset, littleEndian) {
25878                 return dataView.getUint32(dataOffset, littleEndian);
25879             },
25880             size: 4
25881         },
25882         // rational = two long values, first is numerator, second is denominator:
25883         5: {
25884             getValue: function (dataView, dataOffset, littleEndian) {
25885                 return dataView.getUint32(dataOffset, littleEndian) /
25886                     dataView.getUint32(dataOffset + 4, littleEndian);
25887             },
25888             size: 8
25889         },
25890         // slong, 32 bit signed int:
25891         9: {
25892             getValue: function (dataView, dataOffset, littleEndian) {
25893                 return dataView.getInt32(dataOffset, littleEndian);
25894             },
25895             size: 4
25896         },
25897         // srational, two slongs, first is numerator, second is denominator:
25898         10: {
25899             getValue: function (dataView, dataOffset, littleEndian) {
25900                 return dataView.getInt32(dataOffset, littleEndian) /
25901                     dataView.getInt32(dataOffset + 4, littleEndian);
25902             },
25903             size: 8
25904         }
25905     },
25906     
25907     footer : {
25908         STANDARD : [
25909             {
25910                 tag : 'div',
25911                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25912                 action : 'rotate-left',
25913                 cn : [
25914                     {
25915                         tag : 'button',
25916                         cls : 'btn btn-default',
25917                         html : '<i class="fa fa-undo"></i>'
25918                     }
25919                 ]
25920             },
25921             {
25922                 tag : 'div',
25923                 cls : 'btn-group roo-upload-cropbox-picture',
25924                 action : 'picture',
25925                 cn : [
25926                     {
25927                         tag : 'button',
25928                         cls : 'btn btn-default',
25929                         html : '<i class="fa fa-picture-o"></i>'
25930                     }
25931                 ]
25932             },
25933             {
25934                 tag : 'div',
25935                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25936                 action : 'rotate-right',
25937                 cn : [
25938                     {
25939                         tag : 'button',
25940                         cls : 'btn btn-default',
25941                         html : '<i class="fa fa-repeat"></i>'
25942                     }
25943                 ]
25944             }
25945         ],
25946         DOCUMENT : [
25947             {
25948                 tag : 'div',
25949                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25950                 action : 'rotate-left',
25951                 cn : [
25952                     {
25953                         tag : 'button',
25954                         cls : 'btn btn-default',
25955                         html : '<i class="fa fa-undo"></i>'
25956                     }
25957                 ]
25958             },
25959             {
25960                 tag : 'div',
25961                 cls : 'btn-group roo-upload-cropbox-download',
25962                 action : 'download',
25963                 cn : [
25964                     {
25965                         tag : 'button',
25966                         cls : 'btn btn-default',
25967                         html : '<i class="fa fa-download"></i>'
25968                     }
25969                 ]
25970             },
25971             {
25972                 tag : 'div',
25973                 cls : 'btn-group roo-upload-cropbox-crop',
25974                 action : 'crop',
25975                 cn : [
25976                     {
25977                         tag : 'button',
25978                         cls : 'btn btn-default',
25979                         html : '<i class="fa fa-crop"></i>'
25980                     }
25981                 ]
25982             },
25983             {
25984                 tag : 'div',
25985                 cls : 'btn-group roo-upload-cropbox-trash',
25986                 action : 'trash',
25987                 cn : [
25988                     {
25989                         tag : 'button',
25990                         cls : 'btn btn-default',
25991                         html : '<i class="fa fa-trash"></i>'
25992                     }
25993                 ]
25994             },
25995             {
25996                 tag : 'div',
25997                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25998                 action : 'rotate-right',
25999                 cn : [
26000                     {
26001                         tag : 'button',
26002                         cls : 'btn btn-default',
26003                         html : '<i class="fa fa-repeat"></i>'
26004                     }
26005                 ]
26006             }
26007         ],
26008         ROTATOR : [
26009             {
26010                 tag : 'div',
26011                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26012                 action : 'rotate-left',
26013                 cn : [
26014                     {
26015                         tag : 'button',
26016                         cls : 'btn btn-default',
26017                         html : '<i class="fa fa-undo"></i>'
26018                     }
26019                 ]
26020             },
26021             {
26022                 tag : 'div',
26023                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26024                 action : 'rotate-right',
26025                 cn : [
26026                     {
26027                         tag : 'button',
26028                         cls : 'btn btn-default',
26029                         html : '<i class="fa fa-repeat"></i>'
26030                     }
26031                 ]
26032             }
26033         ]
26034     }
26035 });
26036
26037 /*
26038 * Licence: LGPL
26039 */
26040
26041 /**
26042  * @class Roo.bootstrap.DocumentManager
26043  * @extends Roo.bootstrap.Component
26044  * Bootstrap DocumentManager class
26045  * @cfg {String} paramName default 'imageUpload'
26046  * @cfg {String} method default POST
26047  * @cfg {String} url action url
26048  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26049  * @cfg {Boolean} multiple multiple upload default true
26050  * @cfg {Number} thumbSize default 300
26051  * @cfg {String} fieldLabel
26052  * @cfg {Number} labelWidth default 4
26053  * @cfg {String} labelAlign (left|top) default left
26054  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26055  * 
26056  * @constructor
26057  * Create a new DocumentManager
26058  * @param {Object} config The config object
26059  */
26060
26061 Roo.bootstrap.DocumentManager = function(config){
26062     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26063     
26064     this.addEvents({
26065         /**
26066          * @event initial
26067          * Fire when initial the DocumentManager
26068          * @param {Roo.bootstrap.DocumentManager} this
26069          */
26070         "initial" : true,
26071         /**
26072          * @event inspect
26073          * inspect selected file
26074          * @param {Roo.bootstrap.DocumentManager} this
26075          * @param {File} file
26076          */
26077         "inspect" : true,
26078         /**
26079          * @event exception
26080          * Fire when xhr load exception
26081          * @param {Roo.bootstrap.DocumentManager} this
26082          * @param {XMLHttpRequest} xhr
26083          */
26084         "exception" : true,
26085         /**
26086          * @event prepare
26087          * prepare the form data
26088          * @param {Roo.bootstrap.DocumentManager} this
26089          * @param {Object} formData
26090          */
26091         "prepare" : true,
26092         /**
26093          * @event remove
26094          * Fire when remove the file
26095          * @param {Roo.bootstrap.DocumentManager} this
26096          * @param {Object} file
26097          */
26098         "remove" : true,
26099         /**
26100          * @event refresh
26101          * Fire after refresh the file
26102          * @param {Roo.bootstrap.DocumentManager} this
26103          */
26104         "refresh" : true,
26105         /**
26106          * @event click
26107          * Fire after click the image
26108          * @param {Roo.bootstrap.DocumentManager} this
26109          * @param {Object} file
26110          */
26111         "click" : true,
26112         /**
26113          * @event edit
26114          * Fire when upload a image and editable set to true
26115          * @param {Roo.bootstrap.DocumentManager} this
26116          * @param {Object} file
26117          */
26118         "edit" : true,
26119         /**
26120          * @event beforeselectfile
26121          * Fire before select file
26122          * @param {Roo.bootstrap.DocumentManager} this
26123          */
26124         "beforeselectfile" : true,
26125         /**
26126          * @event process
26127          * Fire before process file
26128          * @param {Roo.bootstrap.DocumentManager} this
26129          * @param {Object} file
26130          */
26131         "process" : true
26132         
26133     });
26134 };
26135
26136 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26137     
26138     boxes : 0,
26139     inputName : '',
26140     thumbSize : 300,
26141     multiple : true,
26142     files : [],
26143     method : 'POST',
26144     url : '',
26145     paramName : 'imageUpload',
26146     fieldLabel : '',
26147     labelWidth : 4,
26148     labelAlign : 'left',
26149     editable : true,
26150     delegates : [],
26151     
26152     
26153     xhr : false, 
26154     
26155     getAutoCreate : function()
26156     {   
26157         var managerWidget = {
26158             tag : 'div',
26159             cls : 'roo-document-manager',
26160             cn : [
26161                 {
26162                     tag : 'input',
26163                     cls : 'roo-document-manager-selector',
26164                     type : 'file'
26165                 },
26166                 {
26167                     tag : 'div',
26168                     cls : 'roo-document-manager-uploader',
26169                     cn : [
26170                         {
26171                             tag : 'div',
26172                             cls : 'roo-document-manager-upload-btn',
26173                             html : '<i class="fa fa-plus"></i>'
26174                         }
26175                     ]
26176                     
26177                 }
26178             ]
26179         };
26180         
26181         var content = [
26182             {
26183                 tag : 'div',
26184                 cls : 'column col-md-12',
26185                 cn : managerWidget
26186             }
26187         ];
26188         
26189         if(this.fieldLabel.length){
26190             
26191             content = [
26192                 {
26193                     tag : 'div',
26194                     cls : 'column col-md-12',
26195                     html : this.fieldLabel
26196                 },
26197                 {
26198                     tag : 'div',
26199                     cls : 'column col-md-12',
26200                     cn : managerWidget
26201                 }
26202             ];
26203
26204             if(this.labelAlign == 'left'){
26205                 content = [
26206                     {
26207                         tag : 'div',
26208                         cls : 'column col-md-' + this.labelWidth,
26209                         html : this.fieldLabel
26210                     },
26211                     {
26212                         tag : 'div',
26213                         cls : 'column col-md-' + (12 - this.labelWidth),
26214                         cn : managerWidget
26215                     }
26216                 ];
26217                 
26218             }
26219         }
26220         
26221         var cfg = {
26222             tag : 'div',
26223             cls : 'row clearfix',
26224             cn : content
26225         };
26226         
26227         return cfg;
26228         
26229     },
26230     
26231     initEvents : function()
26232     {
26233         this.managerEl = this.el.select('.roo-document-manager', true).first();
26234         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26235         
26236         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26237         this.selectorEl.hide();
26238         
26239         if(this.multiple){
26240             this.selectorEl.attr('multiple', 'multiple');
26241         }
26242         
26243         this.selectorEl.on('change', this.onFileSelected, this);
26244         
26245         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26246         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26247         
26248         this.uploader.on('click', this.onUploaderClick, this);
26249         
26250         this.renderProgressDialog();
26251         
26252         var _this = this;
26253         
26254         window.addEventListener("resize", function() { _this.refresh(); } );
26255         
26256         this.fireEvent('initial', this);
26257     },
26258     
26259     renderProgressDialog : function()
26260     {
26261         var _this = this;
26262         
26263         this.progressDialog = new Roo.bootstrap.Modal({
26264             cls : 'roo-document-manager-progress-dialog',
26265             allow_close : false,
26266             title : '',
26267             buttons : [
26268                 {
26269                     name  :'cancel',
26270                     weight : 'danger',
26271                     html : 'Cancel'
26272                 }
26273             ], 
26274             listeners : { 
26275                 btnclick : function() {
26276                     _this.uploadCancel();
26277                     this.hide();
26278                 }
26279             }
26280         });
26281          
26282         this.progressDialog.render(Roo.get(document.body));
26283          
26284         this.progress = new Roo.bootstrap.Progress({
26285             cls : 'roo-document-manager-progress',
26286             active : true,
26287             striped : true
26288         });
26289         
26290         this.progress.render(this.progressDialog.getChildContainer());
26291         
26292         this.progressBar = new Roo.bootstrap.ProgressBar({
26293             cls : 'roo-document-manager-progress-bar',
26294             aria_valuenow : 0,
26295             aria_valuemin : 0,
26296             aria_valuemax : 12,
26297             panel : 'success'
26298         });
26299         
26300         this.progressBar.render(this.progress.getChildContainer());
26301     },
26302     
26303     onUploaderClick : function(e)
26304     {
26305         e.preventDefault();
26306      
26307         if(this.fireEvent('beforeselectfile', this) != false){
26308             this.selectorEl.dom.click();
26309         }
26310         
26311     },
26312     
26313     onFileSelected : function(e)
26314     {
26315         e.preventDefault();
26316         
26317         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26318             return;
26319         }
26320         
26321         Roo.each(this.selectorEl.dom.files, function(file){
26322             if(this.fireEvent('inspect', this, file) != false){
26323                 this.files.push(file);
26324             }
26325         }, this);
26326         
26327         this.queue();
26328         
26329     },
26330     
26331     queue : function()
26332     {
26333         this.selectorEl.dom.value = '';
26334         
26335         if(!this.files.length){
26336             return;
26337         }
26338         
26339         if(this.boxes > 0 && this.files.length > this.boxes){
26340             this.files = this.files.slice(0, this.boxes);
26341         }
26342         
26343         this.uploader.show();
26344         
26345         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26346             this.uploader.hide();
26347         }
26348         
26349         var _this = this;
26350         
26351         var files = [];
26352         
26353         var docs = [];
26354         
26355         Roo.each(this.files, function(file){
26356             
26357             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26358                 var f = this.renderPreview(file);
26359                 files.push(f);
26360                 return;
26361             }
26362             
26363             if(file.type.indexOf('image') != -1){
26364                 this.delegates.push(
26365                     (function(){
26366                         _this.process(file);
26367                     }).createDelegate(this)
26368                 );
26369         
26370                 return;
26371             }
26372             
26373             docs.push(
26374                 (function(){
26375                     _this.process(file);
26376                 }).createDelegate(this)
26377             );
26378             
26379         }, this);
26380         
26381         this.files = files;
26382         
26383         this.delegates = this.delegates.concat(docs);
26384         
26385         if(!this.delegates.length){
26386             this.refresh();
26387             return;
26388         }
26389         
26390         this.progressBar.aria_valuemax = this.delegates.length;
26391         
26392         this.arrange();
26393         
26394         return;
26395     },
26396     
26397     arrange : function()
26398     {
26399         if(!this.delegates.length){
26400             this.progressDialog.hide();
26401             this.refresh();
26402             return;
26403         }
26404         
26405         var delegate = this.delegates.shift();
26406         
26407         this.progressDialog.show();
26408         
26409         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26410         
26411         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26412         
26413         delegate();
26414     },
26415     
26416     refresh : function()
26417     {
26418         this.uploader.show();
26419         
26420         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26421             this.uploader.hide();
26422         }
26423         
26424         Roo.isTouch ? this.closable(false) : this.closable(true);
26425         
26426         this.fireEvent('refresh', this);
26427     },
26428     
26429     onRemove : function(e, el, o)
26430     {
26431         e.preventDefault();
26432         
26433         this.fireEvent('remove', this, o);
26434         
26435     },
26436     
26437     remove : function(o)
26438     {
26439         var files = [];
26440         
26441         Roo.each(this.files, function(file){
26442             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26443                 files.push(file);
26444                 return;
26445             }
26446
26447             o.target.remove();
26448
26449         }, this);
26450         
26451         this.files = files;
26452         
26453         this.refresh();
26454     },
26455     
26456     clear : function()
26457     {
26458         Roo.each(this.files, function(file){
26459             if(!file.target){
26460                 return;
26461             }
26462             
26463             file.target.remove();
26464
26465         }, this);
26466         
26467         this.files = [];
26468         
26469         this.refresh();
26470     },
26471     
26472     onClick : function(e, el, o)
26473     {
26474         e.preventDefault();
26475         
26476         this.fireEvent('click', this, o);
26477         
26478     },
26479     
26480     closable : function(closable)
26481     {
26482         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26483             
26484             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26485             
26486             if(closable){
26487                 el.show();
26488                 return;
26489             }
26490             
26491             el.hide();
26492             
26493         }, this);
26494     },
26495     
26496     xhrOnLoad : function(xhr)
26497     {
26498         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26499             el.remove();
26500         }, this);
26501         
26502         if (xhr.readyState !== 4) {
26503             this.arrange();
26504             this.fireEvent('exception', this, xhr);
26505             return;
26506         }
26507
26508         var response = Roo.decode(xhr.responseText);
26509         
26510         if(!response.success){
26511             this.arrange();
26512             this.fireEvent('exception', this, xhr);
26513             return;
26514         }
26515         
26516         var file = this.renderPreview(response.data);
26517         
26518         this.files.push(file);
26519         
26520         this.arrange();
26521         
26522     },
26523     
26524     xhrOnError : function()
26525     {
26526         Roo.log('xhr on error');
26527         
26528         var response = Roo.decode(xhr.responseText);
26529           
26530         Roo.log(response);
26531         
26532         this.arrange();
26533     },
26534     
26535     process : function(file)
26536     {
26537         if(this.fireEvent('process', this, file) !== false){
26538             if(this.editable && file.type.indexOf('image') != -1){
26539                 this.fireEvent('edit', this, file);
26540                 return;
26541             }
26542
26543             this.uploadStart(file, false);
26544
26545             return;
26546         }
26547         
26548     },
26549     
26550     uploadStart : function(file, crop)
26551     {
26552         this.xhr = new XMLHttpRequest();
26553         
26554         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26555             this.arrange();
26556             return;
26557         }
26558         
26559         file.xhr = this.xhr;
26560             
26561         this.managerEl.createChild({
26562             tag : 'div',
26563             cls : 'roo-document-manager-loading',
26564             cn : [
26565                 {
26566                     tag : 'div',
26567                     tooltip : file.name,
26568                     cls : 'roo-document-manager-thumb',
26569                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26570                 }
26571             ]
26572
26573         });
26574
26575         this.xhr.open(this.method, this.url, true);
26576         
26577         var headers = {
26578             "Accept": "application/json",
26579             "Cache-Control": "no-cache",
26580             "X-Requested-With": "XMLHttpRequest"
26581         };
26582         
26583         for (var headerName in headers) {
26584             var headerValue = headers[headerName];
26585             if (headerValue) {
26586                 this.xhr.setRequestHeader(headerName, headerValue);
26587             }
26588         }
26589         
26590         var _this = this;
26591         
26592         this.xhr.onload = function()
26593         {
26594             _this.xhrOnLoad(_this.xhr);
26595         }
26596         
26597         this.xhr.onerror = function()
26598         {
26599             _this.xhrOnError(_this.xhr);
26600         }
26601         
26602         var formData = new FormData();
26603
26604         formData.append('returnHTML', 'NO');
26605         
26606         if(crop){
26607             formData.append('crop', crop);
26608         }
26609         
26610         formData.append(this.paramName, file, file.name);
26611         
26612         if(this.fireEvent('prepare', this, formData) != false){
26613             this.xhr.send(formData);
26614         };
26615     },
26616     
26617     uploadCancel : function()
26618     {
26619         if (this.xhr) {
26620             this.xhr.abort();
26621         }
26622         
26623         
26624         this.delegates = [];
26625         
26626         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26627             el.remove();
26628         }, this);
26629         
26630         this.arrange();
26631     },
26632     
26633     renderPreview : function(file)
26634     {
26635         if(typeof(file.target) != 'undefined' && file.target){
26636             return file;
26637         }
26638         
26639         var previewEl = this.managerEl.createChild({
26640             tag : 'div',
26641             cls : 'roo-document-manager-preview',
26642             cn : [
26643                 {
26644                     tag : 'div',
26645                     tooltip : file.filename,
26646                     cls : 'roo-document-manager-thumb',
26647                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26648                 },
26649                 {
26650                     tag : 'button',
26651                     cls : 'close',
26652                     html : '<i class="fa fa-times-circle"></i>'
26653                 }
26654             ]
26655         });
26656
26657         var close = previewEl.select('button.close', true).first();
26658
26659         close.on('click', this.onRemove, this, file);
26660
26661         file.target = previewEl;
26662
26663         var image = previewEl.select('img', true).first();
26664         
26665         var _this = this;
26666         
26667         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26668         
26669         image.on('click', this.onClick, this, file);
26670         
26671         return file;
26672         
26673     },
26674     
26675     onPreviewLoad : function(file, image)
26676     {
26677         if(typeof(file.target) == 'undefined' || !file.target){
26678             return;
26679         }
26680         
26681         var width = image.dom.naturalWidth || image.dom.width;
26682         var height = image.dom.naturalHeight || image.dom.height;
26683         
26684         if(width > height){
26685             file.target.addClass('wide');
26686             return;
26687         }
26688         
26689         file.target.addClass('tall');
26690         return;
26691         
26692     },
26693     
26694     uploadFromSource : function(file, crop)
26695     {
26696         this.xhr = new XMLHttpRequest();
26697         
26698         this.managerEl.createChild({
26699             tag : 'div',
26700             cls : 'roo-document-manager-loading',
26701             cn : [
26702                 {
26703                     tag : 'div',
26704                     tooltip : file.name,
26705                     cls : 'roo-document-manager-thumb',
26706                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26707                 }
26708             ]
26709
26710         });
26711
26712         this.xhr.open(this.method, this.url, true);
26713         
26714         var headers = {
26715             "Accept": "application/json",
26716             "Cache-Control": "no-cache",
26717             "X-Requested-With": "XMLHttpRequest"
26718         };
26719         
26720         for (var headerName in headers) {
26721             var headerValue = headers[headerName];
26722             if (headerValue) {
26723                 this.xhr.setRequestHeader(headerName, headerValue);
26724             }
26725         }
26726         
26727         var _this = this;
26728         
26729         this.xhr.onload = function()
26730         {
26731             _this.xhrOnLoad(_this.xhr);
26732         }
26733         
26734         this.xhr.onerror = function()
26735         {
26736             _this.xhrOnError(_this.xhr);
26737         }
26738         
26739         var formData = new FormData();
26740
26741         formData.append('returnHTML', 'NO');
26742         
26743         formData.append('crop', crop);
26744         
26745         if(typeof(file.filename) != 'undefined'){
26746             formData.append('filename', file.filename);
26747         }
26748         
26749         if(typeof(file.mimetype) != 'undefined'){
26750             formData.append('mimetype', file.mimetype);
26751         }
26752         
26753         if(this.fireEvent('prepare', this, formData) != false){
26754             this.xhr.send(formData);
26755         };
26756     }
26757 });
26758
26759 /*
26760 * Licence: LGPL
26761 */
26762
26763 /**
26764  * @class Roo.bootstrap.DocumentViewer
26765  * @extends Roo.bootstrap.Component
26766  * Bootstrap DocumentViewer class
26767  * 
26768  * @constructor
26769  * Create a new DocumentViewer
26770  * @param {Object} config The config object
26771  */
26772
26773 Roo.bootstrap.DocumentViewer = function(config){
26774     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26775     
26776     this.addEvents({
26777         /**
26778          * @event initial
26779          * Fire after initEvent
26780          * @param {Roo.bootstrap.DocumentViewer} this
26781          */
26782         "initial" : true,
26783         /**
26784          * @event click
26785          * Fire after click
26786          * @param {Roo.bootstrap.DocumentViewer} this
26787          */
26788         "click" : true,
26789         /**
26790          * @event trash
26791          * Fire after trash button
26792          * @param {Roo.bootstrap.DocumentViewer} this
26793          */
26794         "trash" : true
26795         
26796     });
26797 };
26798
26799 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
26800     
26801     getAutoCreate : function()
26802     {
26803         var cfg = {
26804             tag : 'div',
26805             cls : 'roo-document-viewer',
26806             cn : [
26807                 {
26808                     tag : 'div',
26809                     cls : 'roo-document-viewer-body',
26810                     cn : [
26811                         {
26812                             tag : 'div',
26813                             cls : 'roo-document-viewer-thumb',
26814                             cn : [
26815                                 {
26816                                     tag : 'img',
26817                                     cls : 'roo-document-viewer-image'
26818                                 }
26819                             ]
26820                         }
26821                     ]
26822                 },
26823                 {
26824                     tag : 'div',
26825                     cls : 'roo-document-viewer-footer',
26826                     cn : {
26827                         tag : 'div',
26828                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26829                         cn : [
26830                             {
26831                                 tag : 'div',
26832                                 cls : 'btn-group',
26833                                 cn : [
26834                                     {
26835                                         tag : 'button',
26836                                         cls : 'btn btn-default roo-document-viewer-trash',
26837                                         html : '<i class="fa fa-trash"></i>'
26838                                     }
26839                                 ]
26840                             }
26841                         ]
26842                     }
26843                 }
26844             ]
26845         };
26846         
26847         return cfg;
26848     },
26849     
26850     initEvents : function()
26851     {
26852         
26853         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26854         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26855         
26856         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26857         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26858         
26859         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26860         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26861         
26862         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26863         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26864         
26865         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26866         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26867         
26868         this.bodyEl.on('click', this.onClick, this);
26869         
26870         this.trashBtn.on('click', this.onTrash, this);
26871         
26872     },
26873     
26874     initial : function()
26875     {
26876 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26877         
26878         
26879         this.fireEvent('initial', this);
26880         
26881     },
26882     
26883     onClick : function(e)
26884     {
26885         e.preventDefault();
26886         
26887         this.fireEvent('click', this);
26888     },
26889     
26890     onTrash : function(e)
26891     {
26892         e.preventDefault();
26893         
26894         this.fireEvent('trash', this);
26895     }
26896     
26897 });
26898 /*
26899  * - LGPL
26900  *
26901  * nav progress bar
26902  * 
26903  */
26904
26905 /**
26906  * @class Roo.bootstrap.NavProgressBar
26907  * @extends Roo.bootstrap.Component
26908  * Bootstrap NavProgressBar class
26909  * 
26910  * @constructor
26911  * Create a new nav progress bar
26912  * @param {Object} config The config object
26913  */
26914
26915 Roo.bootstrap.NavProgressBar = function(config){
26916     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26917
26918     this.bullets = this.bullets || [];
26919    
26920 //    Roo.bootstrap.NavProgressBar.register(this);
26921      this.addEvents({
26922         /**
26923              * @event changed
26924              * Fires when the active item changes
26925              * @param {Roo.bootstrap.NavProgressBar} this
26926              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26927              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
26928          */
26929         'changed': true
26930      });
26931     
26932 };
26933
26934 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
26935     
26936     bullets : [],
26937     barItems : [],
26938     
26939     getAutoCreate : function()
26940     {
26941         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26942         
26943         cfg = {
26944             tag : 'div',
26945             cls : 'roo-navigation-bar-group',
26946             cn : [
26947                 {
26948                     tag : 'div',
26949                     cls : 'roo-navigation-top-bar'
26950                 },
26951                 {
26952                     tag : 'div',
26953                     cls : 'roo-navigation-bullets-bar',
26954                     cn : [
26955                         {
26956                             tag : 'ul',
26957                             cls : 'roo-navigation-bar'
26958                         }
26959                     ]
26960                 },
26961                 
26962                 {
26963                     tag : 'div',
26964                     cls : 'roo-navigation-bottom-bar'
26965                 }
26966             ]
26967             
26968         };
26969         
26970         return cfg;
26971         
26972     },
26973     
26974     initEvents: function() 
26975     {
26976         
26977     },
26978     
26979     onRender : function(ct, position) 
26980     {
26981         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26982         
26983         if(this.bullets.length){
26984             Roo.each(this.bullets, function(b){
26985                this.addItem(b);
26986             }, this);
26987         }
26988         
26989         this.format();
26990         
26991     },
26992     
26993     addItem : function(cfg)
26994     {
26995         var item = new Roo.bootstrap.NavProgressItem(cfg);
26996         
26997         item.parentId = this.id;
26998         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
26999         
27000         if(cfg.html){
27001             var top = new Roo.bootstrap.Element({
27002                 tag : 'div',
27003                 cls : 'roo-navigation-bar-text'
27004             });
27005             
27006             var bottom = new Roo.bootstrap.Element({
27007                 tag : 'div',
27008                 cls : 'roo-navigation-bar-text'
27009             });
27010             
27011             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27012             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27013             
27014             var topText = new Roo.bootstrap.Element({
27015                 tag : 'span',
27016                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27017             });
27018             
27019             var bottomText = new Roo.bootstrap.Element({
27020                 tag : 'span',
27021                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27022             });
27023             
27024             topText.onRender(top.el, null);
27025             bottomText.onRender(bottom.el, null);
27026             
27027             item.topEl = top;
27028             item.bottomEl = bottom;
27029         }
27030         
27031         this.barItems.push(item);
27032         
27033         return item;
27034     },
27035     
27036     getActive : function()
27037     {
27038         var active = false;
27039         
27040         Roo.each(this.barItems, function(v){
27041             
27042             if (!v.isActive()) {
27043                 return;
27044             }
27045             
27046             active = v;
27047             return false;
27048             
27049         });
27050         
27051         return active;
27052     },
27053     
27054     setActiveItem : function(item)
27055     {
27056         var prev = false;
27057         
27058         Roo.each(this.barItems, function(v){
27059             if (v.rid == item.rid) {
27060                 return ;
27061             }
27062             
27063             if (v.isActive()) {
27064                 v.setActive(false);
27065                 prev = v;
27066             }
27067         });
27068
27069         item.setActive(true);
27070         
27071         this.fireEvent('changed', this, item, prev);
27072     },
27073     
27074     getBarItem: function(rid)
27075     {
27076         var ret = false;
27077         
27078         Roo.each(this.barItems, function(e) {
27079             if (e.rid != rid) {
27080                 return;
27081             }
27082             
27083             ret =  e;
27084             return false;
27085         });
27086         
27087         return ret;
27088     },
27089     
27090     indexOfItem : function(item)
27091     {
27092         var index = false;
27093         
27094         Roo.each(this.barItems, function(v, i){
27095             
27096             if (v.rid != item.rid) {
27097                 return;
27098             }
27099             
27100             index = i;
27101             return false
27102         });
27103         
27104         return index;
27105     },
27106     
27107     setActiveNext : function()
27108     {
27109         var i = this.indexOfItem(this.getActive());
27110         
27111         if (i > this.barItems.length) {
27112             return;
27113         }
27114         
27115         this.setActiveItem(this.barItems[i+1]);
27116     },
27117     
27118     setActivePrev : function()
27119     {
27120         var i = this.indexOfItem(this.getActive());
27121         
27122         if (i  < 1) {
27123             return;
27124         }
27125         
27126         this.setActiveItem(this.barItems[i-1]);
27127     },
27128     
27129     format : function()
27130     {
27131         if(!this.barItems.length){
27132             return;
27133         }
27134      
27135         var width = 100 / this.barItems.length;
27136         
27137         Roo.each(this.barItems, function(i){
27138             i.el.setStyle('width', width + '%');
27139             i.topEl.el.setStyle('width', width + '%');
27140             i.bottomEl.el.setStyle('width', width + '%');
27141         }, this);
27142         
27143     }
27144     
27145 });
27146 /*
27147  * - LGPL
27148  *
27149  * Nav Progress Item
27150  * 
27151  */
27152
27153 /**
27154  * @class Roo.bootstrap.NavProgressItem
27155  * @extends Roo.bootstrap.Component
27156  * Bootstrap NavProgressItem class
27157  * @cfg {String} rid the reference id
27158  * @cfg {Boolean} active (true|false) Is item active default false
27159  * @cfg {Boolean} disabled (true|false) Is item active default false
27160  * @cfg {String} html
27161  * @cfg {String} position (top|bottom) text position default bottom
27162  * @cfg {String} icon show icon instead of number
27163  * 
27164  * @constructor
27165  * Create a new NavProgressItem
27166  * @param {Object} config The config object
27167  */
27168 Roo.bootstrap.NavProgressItem = function(config){
27169     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27170     this.addEvents({
27171         // raw events
27172         /**
27173          * @event click
27174          * The raw click event for the entire grid.
27175          * @param {Roo.bootstrap.NavProgressItem} this
27176          * @param {Roo.EventObject} e
27177          */
27178         "click" : true
27179     });
27180    
27181 };
27182
27183 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27184     
27185     rid : '',
27186     active : false,
27187     disabled : false,
27188     html : '',
27189     position : 'bottom',
27190     icon : false,
27191     
27192     getAutoCreate : function()
27193     {
27194         var iconCls = 'roo-navigation-bar-item-icon';
27195         
27196         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27197         
27198         var cfg = {
27199             tag: 'li',
27200             cls: 'roo-navigation-bar-item',
27201             cn : [
27202                 {
27203                     tag : 'i',
27204                     cls : iconCls
27205                 }
27206             ]
27207         };
27208         
27209         if(this.active){
27210             cfg.cls += ' active';
27211         }
27212         if(this.disabled){
27213             cfg.cls += ' disabled';
27214         }
27215         
27216         return cfg;
27217     },
27218     
27219     disable : function()
27220     {
27221         this.setDisabled(true);
27222     },
27223     
27224     enable : function()
27225     {
27226         this.setDisabled(false);
27227     },
27228     
27229     initEvents: function() 
27230     {
27231         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27232         
27233         this.iconEl.on('click', this.onClick, this);
27234     },
27235     
27236     onClick : function(e)
27237     {
27238         e.preventDefault();
27239         
27240         if(this.disabled){
27241             return;
27242         }
27243         
27244         if(this.fireEvent('click', this, e) === false){
27245             return;
27246         };
27247         
27248         this.parent().setActiveItem(this);
27249     },
27250     
27251     isActive: function () 
27252     {
27253         return this.active;
27254     },
27255     
27256     setActive : function(state)
27257     {
27258         if(this.active == state){
27259             return;
27260         }
27261         
27262         this.active = state;
27263         
27264         if (state) {
27265             this.el.addClass('active');
27266             return;
27267         }
27268         
27269         this.el.removeClass('active');
27270         
27271         return;
27272     },
27273     
27274     setDisabled : function(state)
27275     {
27276         if(this.disabled == state){
27277             return;
27278         }
27279         
27280         this.disabled = state;
27281         
27282         if (state) {
27283             this.el.addClass('disabled');
27284             return;
27285         }
27286         
27287         this.el.removeClass('disabled');
27288     },
27289     
27290     tooltipEl : function()
27291     {
27292         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27293     }
27294 });
27295  
27296
27297  /*
27298  * - LGPL
27299  *
27300  * FieldLabel
27301  * 
27302  */
27303
27304 /**
27305  * @class Roo.bootstrap.FieldLabel
27306  * @extends Roo.bootstrap.Component
27307  * Bootstrap FieldLabel class
27308  * @cfg {String} html contents of the element
27309  * @cfg {String} tag tag of the element default label
27310  * @cfg {String} cls class of the element
27311  * @cfg {String} target label target 
27312  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27313  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27314  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27315  * @cfg {String} iconTooltip default "This field is required"
27316  * 
27317  * @constructor
27318  * Create a new FieldLabel
27319  * @param {Object} config The config object
27320  */
27321
27322 Roo.bootstrap.FieldLabel = function(config){
27323     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27324     
27325     this.addEvents({
27326             /**
27327              * @event invalid
27328              * Fires after the field has been marked as invalid.
27329              * @param {Roo.form.FieldLabel} this
27330              * @param {String} msg The validation message
27331              */
27332             invalid : true,
27333             /**
27334              * @event valid
27335              * Fires after the field has been validated with no errors.
27336              * @param {Roo.form.FieldLabel} this
27337              */
27338             valid : true
27339         });
27340 };
27341
27342 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27343     
27344     tag: 'label',
27345     cls: '',
27346     html: '',
27347     target: '',
27348     allowBlank : true,
27349     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27350     validClass : 'text-success fa fa-lg fa-check',
27351     iconTooltip : 'This field is required',
27352     
27353     getAutoCreate : function(){
27354         
27355         var cfg = {
27356             tag : this.tag,
27357             cls : 'roo-bootstrap-field-label ' + this.cls,
27358             for : this.target,
27359             cn : [
27360                 {
27361                     tag : 'i',
27362                     cls : '',
27363                     tooltip : this.iconTooltip
27364                 },
27365                 {
27366                     tag : 'span',
27367                     html : this.html
27368                 }
27369             ] 
27370         };
27371         
27372         return cfg;
27373     },
27374     
27375     initEvents: function() 
27376     {
27377         Roo.bootstrap.Element.superclass.initEvents.call(this);
27378         
27379         this.iconEl = this.el.select('i', true).first();
27380         
27381         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27382         
27383         Roo.bootstrap.FieldLabel.register(this);
27384     },
27385     
27386     /**
27387      * Mark this field as valid
27388      */
27389     markValid : function()
27390     {
27391         this.iconEl.show();
27392         
27393         this.iconEl.removeClass(this.invalidClass);
27394         
27395         this.iconEl.addClass(this.validClass);
27396         
27397         this.fireEvent('valid', this);
27398     },
27399     
27400     /**
27401      * Mark this field as invalid
27402      * @param {String} msg The validation message
27403      */
27404     markInvalid : function(msg)
27405     {
27406         this.iconEl.show();
27407         
27408         this.iconEl.removeClass(this.validClass);
27409         
27410         this.iconEl.addClass(this.invalidClass);
27411         
27412         this.fireEvent('invalid', this, msg);
27413     }
27414     
27415    
27416 });
27417
27418 Roo.apply(Roo.bootstrap.FieldLabel, {
27419     
27420     groups: {},
27421     
27422      /**
27423     * register a FieldLabel Group
27424     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27425     */
27426     register : function(label)
27427     {
27428         if(this.groups.hasOwnProperty(label.target)){
27429             return;
27430         }
27431      
27432         this.groups[label.target] = label;
27433         
27434     },
27435     /**
27436     * fetch a FieldLabel Group based on the target
27437     * @param {string} target
27438     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27439     */
27440     get: function(target) {
27441         if (typeof(this.groups[target]) == 'undefined') {
27442             return false;
27443         }
27444         
27445         return this.groups[target] ;
27446     }
27447 });
27448
27449  
27450
27451  /*
27452  * - LGPL
27453  *
27454  * page DateSplitField.
27455  * 
27456  */
27457
27458
27459 /**
27460  * @class Roo.bootstrap.DateSplitField
27461  * @extends Roo.bootstrap.Component
27462  * Bootstrap DateSplitField class
27463  * @cfg {string} fieldLabel - the label associated
27464  * @cfg {Number} labelWidth set the width of label (0-12)
27465  * @cfg {String} labelAlign (top|left)
27466  * @cfg {Boolean} dayAllowBlank (true|false) default false
27467  * @cfg {Boolean} monthAllowBlank (true|false) default false
27468  * @cfg {Boolean} yearAllowBlank (true|false) default false
27469  * @cfg {string} dayPlaceholder 
27470  * @cfg {string} monthPlaceholder
27471  * @cfg {string} yearPlaceholder
27472  * @cfg {string} dayFormat default 'd'
27473  * @cfg {string} monthFormat default 'm'
27474  * @cfg {string} yearFormat default 'Y'
27475
27476  *     
27477  * @constructor
27478  * Create a new DateSplitField
27479  * @param {Object} config The config object
27480  */
27481
27482 Roo.bootstrap.DateSplitField = function(config){
27483     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27484     
27485     this.addEvents({
27486         // raw events
27487          /**
27488          * @event years
27489          * getting the data of years
27490          * @param {Roo.bootstrap.DateSplitField} this
27491          * @param {Object} years
27492          */
27493         "years" : true,
27494         /**
27495          * @event days
27496          * getting the data of days
27497          * @param {Roo.bootstrap.DateSplitField} this
27498          * @param {Object} days
27499          */
27500         "days" : true,
27501         /**
27502          * @event invalid
27503          * Fires after the field has been marked as invalid.
27504          * @param {Roo.form.Field} this
27505          * @param {String} msg The validation message
27506          */
27507         invalid : true,
27508        /**
27509          * @event valid
27510          * Fires after the field has been validated with no errors.
27511          * @param {Roo.form.Field} this
27512          */
27513         valid : true
27514     });
27515 };
27516
27517 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
27518     
27519     fieldLabel : '',
27520     labelAlign : 'top',
27521     labelWidth : 3,
27522     dayAllowBlank : false,
27523     monthAllowBlank : false,
27524     yearAllowBlank : false,
27525     dayPlaceholder : '',
27526     monthPlaceholder : '',
27527     yearPlaceholder : '',
27528     dayFormat : 'd',
27529     monthFormat : 'm',
27530     yearFormat : 'Y',
27531     isFormField : true,
27532     
27533     getAutoCreate : function()
27534     {
27535         var cfg = {
27536             tag : 'div',
27537             cls : 'row roo-date-split-field-group',
27538             cn : [
27539                 {
27540                     tag : 'input',
27541                     type : 'hidden',
27542                     cls : 'form-hidden-field roo-date-split-field-group-value',
27543                     name : this.name
27544                 }
27545             ]
27546         };
27547         
27548         if(this.fieldLabel){
27549             cfg.cn.push({
27550                 tag : 'div',
27551                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27552                 cn : [
27553                     {
27554                         tag : 'label',
27555                         html : this.fieldLabel
27556                     }
27557                 ]
27558             });
27559         }
27560         
27561         Roo.each(['day', 'month', 'year'], function(t){
27562             cfg.cn.push({
27563                 tag : 'div',
27564                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27565             });
27566         }, this);
27567         
27568         return cfg;
27569     },
27570     
27571     inputEl: function ()
27572     {
27573         return this.el.select('.roo-date-split-field-group-value', true).first();
27574     },
27575     
27576     onRender : function(ct, position) 
27577     {
27578         var _this = this;
27579         
27580         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27581         
27582         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27583         
27584         this.dayField = new Roo.bootstrap.ComboBox({
27585             allowBlank : this.dayAllowBlank,
27586             alwaysQuery : true,
27587             displayField : 'value',
27588             editable : false,
27589             fieldLabel : '',
27590             forceSelection : true,
27591             mode : 'local',
27592             placeholder : this.dayPlaceholder,
27593             selectOnFocus : true,
27594             tpl : '<div class="select2-result"><b>{value}</b></div>',
27595             triggerAction : 'all',
27596             typeAhead : true,
27597             valueField : 'value',
27598             store : new Roo.data.SimpleStore({
27599                 data : (function() {    
27600                     var days = [];
27601                     _this.fireEvent('days', _this, days);
27602                     return days;
27603                 })(),
27604                 fields : [ 'value' ]
27605             }),
27606             listeners : {
27607                 select : function (_self, record, index)
27608                 {
27609                     _this.setValue(_this.getValue());
27610                 }
27611             }
27612         });
27613
27614         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27615         
27616         this.monthField = new Roo.bootstrap.MonthField({
27617             after : '<i class=\"fa fa-calendar\"></i>',
27618             allowBlank : this.monthAllowBlank,
27619             placeholder : this.monthPlaceholder,
27620             readOnly : true,
27621             listeners : {
27622                 render : function (_self)
27623                 {
27624                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
27625                         e.preventDefault();
27626                         _self.focus();
27627                     });
27628                 },
27629                 select : function (_self, oldvalue, newvalue)
27630                 {
27631                     _this.setValue(_this.getValue());
27632                 }
27633             }
27634         });
27635         
27636         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27637         
27638         this.yearField = new Roo.bootstrap.ComboBox({
27639             allowBlank : this.yearAllowBlank,
27640             alwaysQuery : true,
27641             displayField : 'value',
27642             editable : false,
27643             fieldLabel : '',
27644             forceSelection : true,
27645             mode : 'local',
27646             placeholder : this.yearPlaceholder,
27647             selectOnFocus : true,
27648             tpl : '<div class="select2-result"><b>{value}</b></div>',
27649             triggerAction : 'all',
27650             typeAhead : true,
27651             valueField : 'value',
27652             store : new Roo.data.SimpleStore({
27653                 data : (function() {
27654                     var years = [];
27655                     _this.fireEvent('years', _this, years);
27656                     return years;
27657                 })(),
27658                 fields : [ 'value' ]
27659             }),
27660             listeners : {
27661                 select : function (_self, record, index)
27662                 {
27663                     _this.setValue(_this.getValue());
27664                 }
27665             }
27666         });
27667
27668         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27669     },
27670     
27671     setValue : function(v, format)
27672     {
27673         this.inputEl.dom.value = v;
27674         
27675         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27676         
27677         var d = Date.parseDate(v, f);
27678         
27679         if(!d){
27680             this.validate();
27681             return;
27682         }
27683         
27684         this.setDay(d.format(this.dayFormat));
27685         this.setMonth(d.format(this.monthFormat));
27686         this.setYear(d.format(this.yearFormat));
27687         
27688         this.validate();
27689         
27690         return;
27691     },
27692     
27693     setDay : function(v)
27694     {
27695         this.dayField.setValue(v);
27696         this.inputEl.dom.value = this.getValue();
27697         this.validate();
27698         return;
27699     },
27700     
27701     setMonth : function(v)
27702     {
27703         this.monthField.setValue(v, true);
27704         this.inputEl.dom.value = this.getValue();
27705         this.validate();
27706         return;
27707     },
27708     
27709     setYear : function(v)
27710     {
27711         this.yearField.setValue(v);
27712         this.inputEl.dom.value = this.getValue();
27713         this.validate();
27714         return;
27715     },
27716     
27717     getDay : function()
27718     {
27719         return this.dayField.getValue();
27720     },
27721     
27722     getMonth : function()
27723     {
27724         return this.monthField.getValue();
27725     },
27726     
27727     getYear : function()
27728     {
27729         return this.yearField.getValue();
27730     },
27731     
27732     getValue : function()
27733     {
27734         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27735         
27736         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27737         
27738         return date;
27739     },
27740     
27741     reset : function()
27742     {
27743         this.setDay('');
27744         this.setMonth('');
27745         this.setYear('');
27746         this.inputEl.dom.value = '';
27747         this.validate();
27748         return;
27749     },
27750     
27751     validate : function()
27752     {
27753         var d = this.dayField.validate();
27754         var m = this.monthField.validate();
27755         var y = this.yearField.validate();
27756         
27757         var valid = true;
27758         
27759         if(
27760                 (!this.dayAllowBlank && !d) ||
27761                 (!this.monthAllowBlank && !m) ||
27762                 (!this.yearAllowBlank && !y)
27763         ){
27764             valid = false;
27765         }
27766         
27767         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27768             return valid;
27769         }
27770         
27771         if(valid){
27772             this.markValid();
27773             return valid;
27774         }
27775         
27776         this.markInvalid();
27777         
27778         return valid;
27779     },
27780     
27781     markValid : function()
27782     {
27783         
27784         var label = this.el.select('label', true).first();
27785         var icon = this.el.select('i.fa-star', true).first();
27786
27787         if(label && icon){
27788             icon.remove();
27789         }
27790         
27791         this.fireEvent('valid', this);
27792     },
27793     
27794      /**
27795      * Mark this field as invalid
27796      * @param {String} msg The validation message
27797      */
27798     markInvalid : function(msg)
27799     {
27800         
27801         var label = this.el.select('label', true).first();
27802         var icon = this.el.select('i.fa-star', true).first();
27803
27804         if(label && !icon){
27805             this.el.select('.roo-date-split-field-label', true).createChild({
27806                 tag : 'i',
27807                 cls : 'text-danger fa fa-lg fa-star',
27808                 tooltip : 'This field is required',
27809                 style : 'margin-right:5px;'
27810             }, label, true);
27811         }
27812         
27813         this.fireEvent('invalid', this, msg);
27814     },
27815     
27816     clearInvalid : function()
27817     {
27818         var label = this.el.select('label', true).first();
27819         var icon = this.el.select('i.fa-star', true).first();
27820
27821         if(label && icon){
27822             icon.remove();
27823         }
27824         
27825         this.fireEvent('valid', this);
27826     },
27827     
27828     getName: function()
27829     {
27830         return this.name;
27831     }
27832     
27833 });
27834
27835